Documentation
¶
Overview ¶
Package protocol is the ObserverLoop wire contract: the signed envelope, the identifier grammar, the NATS subject grammar, the JetStream topology, and the JSON Schema payload set.
It has no dependency on any ObserverLoop implementation repository. Every enumeration in this package is generated from registry.yaml at the root of the containing repository; the only hand-written enumeration is that file.
Index ¶
- Constants
- Variables
- func Canonicalize(raw []byte) ([]byte, error)
- func CheckProtocolVersion(s string) error
- func ParseProtocolVersion(s string) (major, minor int, err error)
- func ParseSubject(subject string) (EventType, SubjectParams, error)
- func SchemaFS() fs.FS
- func SchemaPathFor(t EventType) (string, error)
- func SubjectFor(t EventType, p SubjectParams) (string, error)
- type Capability
- type Discard
- type Durability
- type Envelope
- type EventType
- type Identifier
- type Retention
- type StreamSpec
- type SubjectParams
- type TrustDomain
Constants ¶
const ( ProtocolVersion = "1.3" ProtocolMajor = 1 ProtocolMinor = 3 )
The protocol version this binding implements, from the registry. The subject root carries MAJOR only: an unknown MAJOR is rejected, an unknown MINOR is accepted.
const IdentifierMaxLength = 63
IdentifierMaxLength bounds every identifier. It is chosen so that a canonical lowercase UUIDv7 text form fits unchanged.
const SubjectMaxLength = 255
SubjectMaxLength is the NATS subject limit. The builder enforces it and returns an error rather than publishing a truncated subject.
Variables ¶
var ( // ErrBadIdentifier is returned when a token does not satisfy the identifier // grammar, or when a subject template requires a token that is empty. ErrBadIdentifier = errors.New("protocol: identifier does not satisfy the grammar") // ErrUnknownEventType is returned when a type token is in no registry entry, // and when a subject matches no event type's template. ErrUnknownEventType = errors.New("protocol: unknown event type") // ErrSubjectTooLong is returned when a built subject would exceed the // 255-byte NATS limit. The subject builder never truncates. ErrSubjectTooLong = errors.New("protocol: subject exceeds the 255-byte limit") // ErrSchemaViolation is returned when an envelope or its payload fails the // schema registered for it. ErrSchemaViolation = errors.New("protocol: schema violation") // ErrUnsupportedMajor is returned for an unknown protocol MAJOR version. An // unknown MINOR is accepted, never reported. ErrUnsupportedMajor = errors.New("protocol: unsupported protocol major version") )
The sentinel errors a caller must be able to branch on. Every failure this package reports wraps exactly one of them, so `errors.Is` is sufficient and no caller ever needs to match an error string.
Functions ¶
func Canonicalize ¶
Canonicalize returns the RFC 8785 JSON Canonicalization Scheme form of raw.
This is the only place canonicalisation happens. Signing, verification, and digesting all route through it, so there is no way for two components to disagree about which bytes were signed.
func CheckProtocolVersion ¶
CheckProtocolVersion applies the compatibility rule: an unknown MAJOR is rejected because the subject root carries MAJOR and a MAJOR bump is an enrollment-level operation, while an unknown MINOR is accepted because unknown optional fields are ignored and preserved.
func ParseProtocolVersion ¶
ParseProtocolVersion splits a MAJOR.MINOR version token.
func ParseSubject ¶
func ParseSubject(subject string) (EventType, SubjectParams, error)
ParseSubject is the inverse of SubjectFor. Round-tripping is asserted by a table test over every event type.
It returns ErrUnknownEventType when no template and type token combination matches, which is also the answer for a malformed subject: the protocol has no way to name what such a subject would be.
func SchemaFS ¶
SchemaFS exposes the embedded schema tree so that a consumer can serve or re-validate the published schemas without vendoring them. Paths are rooted at "schemas", matching the paths SchemaPathFor returns.
func SchemaPathFor ¶
SchemaPathFor returns the payload schema path registered for t, relative to the repository root and to the embedded schema filesystem alike.
func SubjectFor ¶
func SubjectFor(t EventType, p SubjectParams) (string, error)
SubjectFor builds the subject for t. It returns an error when t is unknown, when a token the template requires is empty or malformed, or when the result would exceed SubjectMaxLength. It never truncates.
Types ¶
type Capability ¶
type Capability string
Capability is a token a signer must hold, resolved through its delegation chain, before an event type may be accepted from it.
const ( CapabilityAgentLifecycle Capability = "agent.lifecycle" CapabilityAuditWrite Capability = "audit.write" CapabilityCommandExecute Capability = "command.execute" CapabilityCommandIssue Capability = "command.issue" CapabilityInteractionRequest Capability = "interaction.request" CapabilityInteractionResolve Capability = "interaction.resolve" CapabilityThreadWrite Capability = "thread.write" )
func CapabilitiesFor ¶
func CapabilitiesFor(t EventType) []Capability
CapabilitiesFor returns the capabilities a signer must hold for t, or nil when t is unknown. The caller receives a copy.
type Discard ¶
type Discard string
Discard is a JetStream discard policy: which message is dropped when a limit is reached.
const (
DiscardOld Discard = "old"
)
type Durability ¶
type Durability string
Durability selects the transport guarantee for an event type.
const ( // DurabilityJetStream events are published to a JetStream stream and are // redelivered until acknowledged. DurabilityJetStream Durability = "jetstream" // DurabilityCore events are published to core NATS with no stream behind // them; they are request-scoped and are not replayed. DurabilityCore Durability = "core" )
func DurabilityFor ¶
func DurabilityFor(t EventType) Durability
DurabilityFor returns the transport guarantee registered for t. An unknown type has no registered durability and returns the empty Durability, which is never a publishable value.
type Envelope ¶
type Envelope struct {
ProtocolVersion string `json:"protocol_version"`
EventID Identifier `json:"event_id"`
EventType EventType `json:"event_type"`
TrustDomain TrustDomain `json:"trust_domain"`
TenantID Identifier `json:"tenant_id"`
WorkspaceID Identifier `json:"workspace_id"`
ThreadID *Identifier `json:"thread_id,omitempty"`
ActivityID *Identifier `json:"activity_id,omitempty"`
SourceConductorID *Identifier `json:"source_conductor_id,omitempty"`
ConductorEpoch *int64 `json:"conductor_epoch,omitempty"`
SourceAgentID *Identifier `json:"source_agent_id,omitempty"`
DestinationConductorID *Identifier `json:"destination_conductor_id,omitempty"`
DestinationAgentID *Identifier `json:"destination_agent_id,omitempty"`
DefinitionDigest *string `json:"definition_digest,omitempty"`
OccurredAt time.Time `json:"occurred_at"`
Sequence int64 `json:"sequence"`
CorrelationID Identifier `json:"correlation_id"`
CausationID *Identifier `json:"causation_id,omitempty"`
ExpiresAt *time.Time `json:"expires_at,omitempty"`
Payload json.RawMessage `json:"payload"`
SignerKeyID string `json:"signer_key_id"`
DelegationChain []string `json:"delegation_chain"`
Signature string `json:"signature"`
// contains filtered or unexported fields
}
Envelope is the signed unit of the protocol. Field order matches the specification; the wire encoding is key-sorted, because RFC 8785 sorts.
func (Envelope) MarshalJSON ¶
MarshalJSON encodes the envelope with timestamps normalised to UTC at millisecond precision. Every other field is encoded from its struct tag, so the field set is declared exactly once.
func (*Envelope) SigningBytes ¶
SigningBytes returns the RFC 8785 canonical form of the envelope with signature omitted. This is the exact byte sequence that is signed and verified; nothing else may be signed.
func (*Envelope) UnmarshalJSON ¶
UnmarshalJSON decodes a wire envelope, keeps any field this binding does not know about, and rejects a timestamp that is not exactly RFC 3339 UTC at millisecond precision. The strictness is deliberate: Go's time decoder would happily accept a coarser or finer form, re-encode it in the canonical one, and produce signing bytes that differ from the bytes the signer signed.
func (*Envelope) Validate ¶
Validate checks the envelope against the embedded envelope schema and the payload against the schema registered for EventType. It also applies the two rules the schemas deliberately do not restate: the protocol MAJOR must be supported, and every identifier must satisfy the grammar - the schemas carry only the length bound, because ParseIdentifier is the single validator.
It does not verify the signature. That is the verifier's job, and it needs a key store this package must not depend on.
type EventType ¶
type EventType string
EventType is a registry type token. It is simultaneously the envelope event_type, the trailing token of the subject, and the payload schema basename, so the three can never drift.
const ( EventAgentLifecycle EventType = "agent.lifecycle" EventAuditIncident EventType = "audit.incident" EventAuditSchema EventType = "audit.schema" EventAuditSecurity EventType = "audit.security" EventAuditSignature EventType = "audit.signature" EventChatMessage EventType = "chat.message" EventCommandRequested EventType = "command.requested" EventCommandResult EventType = "command.result" EventDecisionRecord EventType = "decision.record" EventPermissionRequested EventType = "permission.requested" EventPermissionResolved EventType = "permission.resolved" EventQuestionRequested EventType = "question.requested" EventQuestionResolved EventType = "question.resolved" EventToolCall EventType = "tool.call" EventToolResult EventType = "tool.result" )
func AllEventTypes ¶
func AllEventTypes() []EventType
AllEventTypes returns every registry type token, sorted. The caller receives a copy: the generated table is not exposed to mutation.
func ParseEventType ¶
ParseEventType reports whether s is a registry type token, and returns it. It is a lookup, not a parse: the permitted set is generated from the registry and is closed.
type Identifier ¶
type Identifier string
Identifier is a validated, NATS-subject-safe token. The zero value is not a valid identifier; construct one only through ParseIdentifier.
func ParseIdentifier ¶
func ParseIdentifier(s string) (Identifier, error)
ParseIdentifier is the single validation point for every identifier in the system. It returns ErrBadIdentifier for anything that does not match ^[a-z0-9-]{1,63}$ - including the empty string, uppercase, dots, NATS wildcards, whitespace, and anything longer than 63 bytes.
func (Identifier) String ¶
func (i Identifier) String() string
String returns the token. It exists so an Identifier can be used where a fmt.Stringer is expected without an explicit conversion.
type StreamSpec ¶
type StreamSpec struct {
Name string
Subjects []string
Retention Retention
MaxAge time.Duration
Discard Discard
Replicas int
DuplicateWindow time.Duration
}
StreamSpec is a JetStream stream, resolved for one tenant and workspace. It is deliberately a plain description rather than a broker call: this module must not depend on a NATS client.
func StreamsFor ¶
func StreamsFor(tenant, workspace Identifier, replicas int) []StreamSpec
StreamsFor resolves every stream template for one workspace. The subject bindings are generated from the subject grammar, so a stream can never listen on a subject the grammar does not produce.
The identifiers are substituted as given: they are already validated by the time a caller holds an Identifier, and this function has no error to return per the published surface.
type SubjectParams ¶
type SubjectParams struct {
TenantID Identifier
WorkspaceID Identifier
ThreadID Identifier
AgentID Identifier
ConductorID Identifier
CommandID Identifier
}
SubjectParams carries every token a subject template can interpolate. Templates ignore the fields they do not use.
type TrustDomain ¶
type TrustDomain string
TrustDomain names the class of key that signed an envelope. Every envelope declares exactly one.
const ( TrustDomainSaaSAttested TrustDomain = "saas-attested" TrustDomainTenant TrustDomain = "tenant" )
func TrustDomainsFor ¶
func TrustDomainsFor(t EventType) []TrustDomain
TrustDomainsFor returns the trust domains permitted to sign t, or nil when t is unknown. The caller receives a copy.