Documentation
¶
Overview ¶
Package trailrpc is the audit interceptor for the daemon's Connect services: a connect.Interceptor that records MUTATING unary RPCs to the activity trail by construction, so auditing a state change is a structural default of the mount rather than a per-handler line a developer must remember to add.
WHY AN INTERCEPTOR. The trail's other producers are hand-placed trail.Append calls at scattered callsites; a new mutating handler that forgets the call is silently unaudited (the gap this closes for the token service, whose RevokeToken recorded nothing). Wrapping the SERVICE means every method on it - including one added later - passes through here. Two properties matter for an audit boundary a hostile contributor cannot quietly sidestep:
- The actor is SERVER-STAMPED at the mount (the tier the guard already enforces), never read from a caller-supplied field, so a handler cannot forge who acted.
- Classification is fail-CLOSED: a method whose verb this package does not recognize is treated as mutating and recorded, so a novel RPC is over-audited, never silently skipped. TestKnownVerbs (the arch ratchet) additionally fails CI if any mounted service grows a method with an unclassified verb, forcing the author to place it in one bucket or the other.
It is deliberately NARROW today: mounted only on TokenService (whose RevokeToken mutation lacked any producer). The audit-trail assessment (session plans) tracks extending it to the other mutating services and the reconciliation that needs (jobs already record on completion; memory edits would need a new wire Kind) - kept out of this doc so it does not rot against current method names.
KNOWN LIMIT of verb classification: a method's leading word is matched EXACTLY, so "Listen" is not mistaken for the read verb "List" (it falls to unclassified -> recorded -> flagged by the arch test). What exact-word matching still cannot catch is a COMPOUND verb whose first word is a read verb but whose action mutates (a hypothetical "ExportAndReset"): it would classify read and skip. The convention this codebase follows - one leading verb per method - keeps that out of reach, and the arch test surfaces any novel verb; but a reviewer adding a compound method must place it deliberately.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Interceptor ¶
Interceptor records every MUTATING unary call on the service it wraps to the trail under trailDir, with the server-stamped actor and the given kind (one wire Kind per mounted service - the token service is all token_lifecycle). Reads are not recorded by default (the trail is for consequential actions, not queries); pass WithAuditReads to also record read verbs, as the memory service does. Recording is best-effort and post-hoc: it never blocks or fails the RPC - trail.Append swallows I/O errors, matching the trail's "never a precondition for the action it records" contract - and a failed mutation is still recorded, with its error, because an attempted revoke is itself worth auditing.
Types ¶
type Option ¶
type Option func(*options)
Option configures an Interceptor. See WithAuditReads.
func WithAuditReads ¶
func WithAuditReads() Option
WithAuditReads makes the interceptor record READ calls (Get/List/...) in addition to mutations. It is off by default because the trail is for consequential actions, not queries - but the memory service opts in, since a read of the agent's own handoff journal is itself worth auditing there. The token service does NOT set it, so its ListTokens stays unrecorded.