Observers¶
Structured reports of connections and protocol anomalies. Connection and anomaly reports is the guide; this is the reference.
py1815.observers ¶
Structured reports of connections and protocol anomalies, for the host application.
The outstation and the master log what happens to their connections, and the session logs a frame or a fragment it drops. A log line is for a person. A host application that keeps a record of every connection attempt, or that passes protocol anomalies to its own security monitoring, needs the same facts as fields. This module defines those records and the type of the callable that receives each one.
- :class:
ConnectionEventis one thing that happened to a connection: accepted, refused, displaced or ended at the outstation's listener, and connected, failed or lost at the master. :class:~py1815.server.OutstationServerand :class:~py1815.master.api.Outstationtake aconnection_observer. - :class:
ProtocolAnomalyis one frame, fragment or exchange the session dropped or gave up on, with a stable :class:AnomalyCodeand a :class:AnomalySeverity. :class:~py1815.session.Sessiontakes ananomaly_observer.
Each record is a frozen dataclass of plain values, so it can be logged, queued
or converted to JSON with :func:dataclasses.asdict. The log lines are written
as before; an observer is an addition to them, not a replacement.
An observer is called on the event loop, or for a session on whatever thread called it, and must return quickly. An observer that raises does not affect the connection or the session: the first failure of an observer is logged with its traceback, and later failures of the same observer are logged at DEBUG.
Copyright 2026 DER Security Corp. Licensed under the Apache License, Version 2.0.
ConnectionRole ¶
Bases: StrEnum
Which end of the association reported a connection event.
Source code in src/py1815/observers.py
ConnectionDirection ¶
Bases: StrEnum
Which end opened the connection, seen from the end that reports it.
Source code in src/py1815/observers.py
ConnectionTransport ¶
ConnectionOutcome ¶
Bases: StrEnum
What happened to a connection.
Source code in src/py1815/observers.py
Endpoint
dataclass
¶
An address and port at one end of a connection.
Source code in src/py1815/observers.py
of
classmethod
¶
Return the endpoint of a socket name, or None when the name holds none.
name is what getpeername or getsockname returns: a tuple
whose first two items are the address and the port.
Source code in src/py1815/observers.py
ConnectionEvent
dataclass
¶
One thing that happened to one connection.
Attributes:
| Name | Type | Description |
|---|---|---|
role |
ConnectionRole
|
The end that reports the event. |
direction |
ConnectionDirection
|
Which end opened the connection. |
outcome |
ConnectionOutcome
|
What happened. |
transport |
ConnectionTransport
|
TCP, or TLS over TCP. |
remote |
Endpoint | None
|
The other end. For a master's failed attempt, the host and port it tried. |
local |
Endpoint | None
|
This end, or None when no socket was made. |
peer_identity |
str | None
|
For a TLS connection, who the peer proved to be: at the
outstation, the allow-list entry the master's certificate matched;
at the master, the name the outstation's certificate was checked
against, or the certificate's |
reason |
str
|
Why, in a few words, for every outcome except |
name |
str | None
|
At the master, the name the outstation was added under. None at the outstation. |
timestamp |
float
|
When the event was reported, in seconds since the Unix epoch. |
Source code in src/py1815/observers.py
AnomalyCode ¶
Bases: StrEnum
What the session dropped or gave up on. The values are stable.
Source code in src/py1815/observers.py
AnomalySeverity ¶
Bases: StrEnum
How much an anomaly says about the peer, from least to most.
Source code in src/py1815/observers.py
ProtocolAnomaly
dataclass
¶
One frame, fragment or exchange the session dropped or gave up on.
Attributes:
| Name | Type | Description |
|---|---|---|
code |
AnomalyCode
|
What happened. |
severity |
AnomalySeverity
|
How much it says about the peer. Always |
message |
str
|
The same text the session logs, without the |
source |
int | None
|
The link address the frame came from, or None when the anomaly concerns no single frame. |
destination |
int | None
|
The link address the frame was sent to, or None. |
peer |
Endpoint | None
|
The connection the octets arrived on, or the sender of a broadcast datagram, when the session was told. None for a session driven without a listener. |
timestamp |
float
|
When the anomaly was reported, in seconds since the Unix epoch. |
Source code in src/py1815/observers.py
ObserverGuard ¶
Call an observer and keep its exceptions from reaching the caller.
The first exception of an observer is logged with its traceback. Later exceptions of the same observer are logged at DEBUG, so an observer that fails on every record does not fill the log. A different observer starts over.
Source code in src/py1815/observers.py
deliver ¶
Call observer with record, unless there is no observer.