Documentation
¶
Overview ¶
Package daemon assembles the obsigna-daemon's components — chain state, key source, receipt store, frame socket — into a single Run entrypoint. cmd/obsigna-daemon/main.go wraps Run with flag/env parsing and signal handling.
Index ¶
- Constants
- func DefaultConfigPath() string
- func DefaultDBPath() string
- func DefaultForensicKeyPath() string
- func DefaultForensicPublicKeyPath(keyPath string) string
- func DefaultKeyPath() string
- func DefaultPublicKeyPath(keyPath string) string
- func DefaultSocketPath() string
- func GenerateForensicKey(keyPath, publicKeyPath string) (string, error)
- func GenerateKey(keyPath, publicKeyPath string) error
- func Run(ctx context.Context, cfg Config) error
- type Config
- type DisclosureConfig
- type Duration
- type FileConfig
- type ProtocolVersion
- type RotateSummary
- type VersionRange
Constants ¶
const ( DefaultIssuerID = "did:agent-receipts-daemon:local" DefaultVerificationMethodID = DefaultIssuerID + "#k1" )
DefaultIssuerID and DefaultVerificationMethodID are the identity defaults a local daemon signs under when no issuer/verification-method is configured. They are exported so any tool that builds a Config for the same local install — the daemon's own config resolution and `obsigna keys rotate` — shares one source of truth: the issuer DID and verification method are embedded in every signed receipt, so two callers defaulting them differently would fork chain identity for the same operator.
const RotatedPublicKeySuffix = ".rotated-"
RotatedPublicKeySuffix is the filename suffix archiveOldPublicKey appends to a superseded public key (followed by a short key fingerprint), e.g. signing.key.pub.rotated-<fp>. The verify CLI scans for it to rediscover a rotated chain's genesis key, so the writer here and that reader share this one definition rather than hardcoding the literal on each side.
Variables ¶
This section is empty.
Functions ¶
func DefaultConfigPath ¶ added in v0.14.0
func DefaultConfigPath() string
DefaultConfigPath returns the per-user TOML config path used when --config is not given: $XDG_DATA_HOME/agent-receipts/daemon.toml, co-located with receipts.db and the signing key (DefaultDBPath/DefaultKeyPath). Returns "" when the XDG data home cannot be resolved (no XDG_DATA_HOME and no home directory), matching the other Default*Path helpers.
func DefaultDBPath ¶
func DefaultDBPath() string
DefaultDBPath returns the per-user SQLite path used when AGENTRECEIPTS_DB is not set. Uses XDG_DATA_HOME (defaults to ~/.local/share on Linux/macOS).
func DefaultForensicKeyPath ¶ added in v0.15.0
func DefaultForensicKeyPath() string
DefaultForensicKeyPath returns the default forensic private-key path, co-located with the signing key and receipt store. Empty when the XDG data home cannot be resolved, matching the other Default*Path helpers.
func DefaultForensicPublicKeyPath ¶ added in v0.15.0
DefaultForensicPublicKeyPath returns the default forensic public-key path: keyPath with the ".pub" suffix. Empty when keyPath is empty.
func DefaultKeyPath ¶
func DefaultKeyPath() string
DefaultKeyPath returns the per-user signing-key path used when AGENTRECEIPTS_KEY is not set. Uses XDG_DATA_HOME (defaults to ~/.local/share on Linux/macOS).
func DefaultPublicKeyPath ¶
DefaultPublicKeyPath returns the default published public-key path: the same directory as keyPath with the suffix ".pub". Empty when keyPath is empty so cmd/main.go can surface a clearer "Config.KeyPath is required" error from validateConfig instead of a less-helpful PublicKeyPath one.
func DefaultSocketPath ¶
func DefaultSocketPath() string
DefaultSocketPath returns the per-OS default socket path. Phase 1 resolves Q1 of issue #236; the macOS default was reworked again for issue #545:
- macOS: $XDG_DATA_HOME/agent-receipts/events.sock — per-user, unprivileged. Defaults to $HOME/.local/share/agent-receipts/events.sock when XDG_DATA_HOME is unset, co-located with receipts.db and the signing key.
- Linux with $XDG_RUNTIME_DIR set: $XDG_RUNTIME_DIR/agentreceipts/ events.sock — per-user, unprivileged.
- Linux fallback (no $XDG_RUNTIME_DIR): /run/agentreceipts/events.sock — this is the system-install path and requires privileged directory creation/write. Unprivileged users on systems without $XDG_RUNTIME_DIR should set AGENTRECEIPTS_SOCKET explicitly.
- Other platforms: empty string (the daemon refuses to start outside Linux/macOS, see Run).
The OS resolution is delegated to emitter.DefaultSocketPath so the daemon and the SDK's emitter cannot drift on what counts as the per-user default socket path. emitter.DefaultSocketPath additionally honours AGENTRECEIPTS_SOCKET when set — that branch is a no-op for the daemon binary, which independently consults AGENTRECEIPTS_SOCKET in main before calling this function, but it keeps any library consumer aligned with the emitter without needing to re-implement the env-var fallback.
func GenerateForensicKey ¶ added in v0.15.0
GenerateForensicKey creates a new X25519 forensic key pair (ADR-0012) and writes the raw 32-byte private key to keyPath (mode 0600) and the raw 32-byte public key to publicKeyPath (mode 0644). It returns the public key's canonical fingerprint (ADR-0015, sha256:<hex>) for display so an operator can confirm the key the daemon will encrypt to matches the private key they keep for recovery.
Like GenerateKey, it refuses to overwrite or follow a symlink at either path, and rolls back the private key if the public-key write fails. The two keys have deliberately separate lifecycles: the public key goes in daemon config; the private key is kept offline by the forensic responder and never given to the daemon.
func GenerateKey ¶
GenerateKey creates a new Ed25519 key pair and saves the private key to keyPath (mode 0600) and public key to publicKeyPath (mode 0644). Refuses to overwrite an existing file at either path, and refuses to follow a symlink at either path. Use this explicitly via --init; never call it as a side-effect of starting the daemon — silently regenerating a missing key would invalidate every receipt previously signed by the operator's real key.
Atomicity: both files are created with O_CREATE|O_EXCL|O_NOFOLLOW so an attacker who plants a symlink (or any other dirent) at either path between the directory creation and the file open trips O_EXCL — we never write through the symlink target. If the public-key write fails after the private-key write succeeded, the private-key file is removed so the caller doesn't end up with a half-initialised on-disk state.
The mode passed to OpenFile may be narrowed by the process umask; an explicit fchmod after open ensures the on-disk mode matches what the caller asked for.
Types ¶
type Config ¶
type Config struct {
// SocketPath is the Unix-domain socket the daemon listens on.
SocketPath string
// UnsafeSocketPath permits a SocketPath outside the per-platform safe set
// (see checkSocketPath). When false (the default), Run refuses to start on
// an unsafe explicit override; when true, Run starts and warns periodically.
// Set from --unsafe-socket-path. TCP addresses are rejected regardless.
UnsafeSocketPath bool
// DBPath is the SQLite receipt-store path.
DBPath string
// KeyPath is the PEM-encoded Ed25519 private key path. Mode must be 0600.
KeyPath string
// PublicKeyPath is where the daemon publishes the matching SPKI public
// key in PEM form, mode 0644, on every startup. Read-side tools
// (`agent-receipts verify`) load it without needing access to KeyPath or
// the daemon's signing surface. Defaults to KeyPath + ".pub" when empty.
PublicKeyPath string
// ForensicPublicKeyPath is the path to the X25519 forensic public key
// (32 raw bytes) used to encrypt action parameters (ADR-0012, HPKE envelope).
// When set, incoming tool parameters are encrypted before signing and
// attached as action.parameters_disclosure. When empty, parameters are
// hashed only (the default, privacy-preserving). The private key is held
// offline by the forensic responder. Set from --forensic-public-key.
ForensicPublicKeyPath string
// ChainID is the chain id all incoming frames are written under. Phase 1
// supports one chain per daemon process.
ChainID string
// IssuerID is embedded in receipts as issuer.id, e.g.
// "did:agent-receipts-daemon:<host>".
IssuerID string
// VerificationMethodID goes into proof.verificationMethod.
VerificationMethodID string
// AnchorLogPath, when set, is an append-only external-witness log that
// rotation events are written to before the local chain commits (ADR-0015
// anchor-first ordering). Empty disables anchoring — the operator keeps the
// chain-integrity guarantees but forgoes the post-compromise integrity
// guarantee. Set from --anchor-log (env: AGENTRECEIPTS_ANCHOR_LOG).
AnchorLogPath string
// Logger receives daemon log lines. Defaults to log.Default().
Logger *log.Logger
// TraceLog optionally receives daemon trace lines for test debugging.
// When nil, tracing is silent. Tests can pass a buffer to inspect
// what frames were received, receipts signed, etc.
TraceLog io.Writer
// ParameterDisclosure selects which actions have their parameters encrypted
// into the parameters_disclosure envelope (ADR-0012). Value space mirrors the
// OpenClaw plugin: "false"/"off"/"" (default, hash only), "true"/"all",
// "high" (high- and critical-risk actions), or a comma-separated allowlist of
// action types. Disclosure also requires ForensicPublicKeyPath — without a
// key there is nothing to encrypt to. Parsed via
// pipeline.ParseDisclosurePolicy at startup.
ParameterDisclosure string
// RedactPatternsPath is an optional path to a YAML file of additional
// redaction patterns applied to receipt body fields after hashing. When
// empty, only the built-in patterns are used. File format:
//
// patterns:
// - name: my-secret
// pattern: 'MY_SECRET_[A-Z0-9]+'
RedactPatternsPath string
// ShutdownDeadline is the total time budget for emitting interrupted-chain
// terminators on SIGTERM/SIGINT. Defaults to 200ms when zero.
ShutdownDeadline time.Duration
}
Config is the daemon's startup configuration. Resolve from flags/env in cmd/obsigna-daemon/main.go and pass to Run.
type DisclosureConfig ¶ added in v0.15.0
type DisclosureConfig struct {
Value string
}
DisclosureConfig is the parsed `parameter_disclosure` config-file value. It normalises three accepted TOML shapes into the policy string consumed by pipeline.ParseDisclosurePolicy:
- boolean: true → "true", false → "false". Preserves backwards compatibility with configs (and the documented default) written when parameter_disclosure was a bool, instead of failing to decode.
- string: a policy keyword ("false"/"true"/"high") or a comma-separated action-type allowlist, used verbatim.
- array of strings: an action-type allowlist, joined with commas — the natural TOML/JSON spelling of the list form.
The flag and environment-variable layers only accept the string spelling (they have no array type); the array form is a TOML convenience.
func (*DisclosureConfig) UnmarshalTOML ¶ added in v0.15.0
func (d *DisclosureConfig) UnmarshalTOML(v any) error
UnmarshalTOML implements toml.Unmarshaler so the parameter_disclosure key can be a boolean, a string, or an array of strings.
type Duration ¶ added in v0.14.0
Duration wraps time.Duration so it decodes from a TOML string such as "200ms" or "1s" via Go's time.ParseDuration. BurntSushi/toml has no native duration type; without this an operator would have to write nanoseconds.
func (*Duration) UnmarshalText ¶ added in v0.14.0
UnmarshalText implements encoding.TextUnmarshaler so toml.DecodeFile parses a quoted duration string into a time.Duration. An empty string is rejected — a key present in the file but blank is a misconfiguration, not a default.
type FileConfig ¶ added in v0.14.0
type FileConfig struct {
Socket *string `toml:"socket"`
DB *string `toml:"db"`
Key *string `toml:"key"`
PublicKey *string `toml:"public_key"`
ForensicPublicKey *string `toml:"forensic_public_key"`
ChainID *string `toml:"chain_id"`
IssuerID *string `toml:"issuer_id"`
VerificationMethod *string `toml:"verification_method"`
ParameterDisclosure *DisclosureConfig `toml:"parameter_disclosure"`
RedactPatterns *string `toml:"redact_patterns"`
UnsafeSocketPath *bool `toml:"unsafe_socket_path"`
// ShutdownDeadline accepts a Go duration string, e.g. "200ms" or "1s".
ShutdownDeadline *Duration `toml:"shutdown_deadline"`
}
FileConfig is the subset of Config that can be set from the TOML config file. Every field mirrors an existing flag/env var so operators have one mental model: the TOML key is the flag name with dashes turned into underscores. Pointer-typed fields distinguish "absent in the file" (nil, so a lower-precedence default/env/flag wins) from "explicitly set to the zero value" (e.g. unsafe_socket_path = false) — the config file is the lowest-priority layer, so an absent key must never clobber env or flags.
func LoadConfigFile ¶ added in v0.14.0
func LoadConfigFile(path string, required bool) (*FileConfig, error)
LoadConfigFile reads and strictly decodes the TOML config at path.
- required=false (default-path load): a missing file is not an error — it returns (nil, nil) so the daemon runs on flags/env alone. Any other read or parse error is returned, because a present-but-broken config is a misconfiguration we refuse to silently ignore.
- required=true (explicit --config): a missing file IS an error — the operator named a path that does not exist, which is almost certainly a typo rather than an intentional "no config".
Unknown keys are rejected: a typo'd key (e.g. "sockett") would otherwise be silently ignored, leaving the daemon running with a different config than the operator believes they set. This mirrors the redact-pattern loader's "reject malformed config rather than silently degrade" stance.
type ProtocolVersion ¶ added in v0.14.0
type ProtocolVersion struct {
// FrameVersion is the range of emitter-frame schema versions (the `v`
// field on the wire) the daemon can interpret.
FrameVersion VersionRange `json:"frame_version"`
}
ProtocolVersion describes the wire protocol this daemon speaks. It is the machine-readable surface ADR-0024 Gate #8 reads (via the `obsigna-daemon --protocol-version` flag) to assert the released daemon's spoken range intersects the range each released SDK declares it can emit.
func SpokenProtocolVersion ¶ added in v0.14.0
func SpokenProtocolVersion() ProtocolVersion
SpokenProtocolVersion returns the daemon's spoken protocol range, sourced from the pipeline that actually accepts frames so the declaration cannot drift from the bytes the daemon honours.
type RotateSummary ¶ added in v0.22.0
type RotateSummary struct {
ChainID string
Sequence int
ReceiptID string
OldFingerprint string
NewFingerprint string
ArchivedPublicKey string
AnchoredTo string
}
RotateSummary reports the outcome of a key rotation for the CLI to print.
func RotateKey ¶ added in v0.22.0
func RotateKey(cfg Config) (RotateSummary, error)
RotateKey rotates the daemon's signing key (ADR-0015 Phase A, offline).
It loads the current ("outgoing") key, generates a new ("incoming") key, appends a key_rotated receipt — signed with the outgoing key — to the head of cfg.ChainID, archives the outgoing public key so historical receipts stay verifiable, and swaps the incoming key into place. After this returns the daemon must be (re)started to pick up the new key; the rotated chain verifies end-to-end only when the verifier starts from the *genesis* public key and traverses the rotation (spec §7.3.7), not from the freshly published key.
The daemon MUST be stopped first: a running daemon holds the outgoing key in memory and would keep signing with it while the chain records a handover to the incoming key. RotateKey refuses to run when its socket is reachable.
Ordering is chosen so a committed rotation receipt always matches the on-disk key: the rotation receipt is signed in memory with the outgoing key, the key files are swapped (with rollback on failure), and only then is the receipt inserted (rolling the key files back if the insert fails). The residual window — a crash between the key swap and the insert — is the gap the external anchor (--anchor-log, written before any local change) closes.
type VersionRange ¶ added in v0.14.0
VersionRange is an inclusive range of integer protocol versions. An empty intersection between two ranges means the two peers cannot talk.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
agent-receipts
command
Command agent-receipts is the deprecation shim for the renamed `obsigna` CLI (ADR-0030).
|
Command agent-receipts is the deprecation shim for the renamed `obsigna` CLI (ADR-0030). |
|
obsigna
command
Command obsigna is the Agent Receipts read-side CLI.
|
Command obsigna is the Agent Receipts read-side CLI. |
|
obsigna-daemon
command
Command obsigna-daemon runs the receipts daemon: a single OS-user process that owns the Ed25519 signing key and the SQLite receipt store, and receives fire-and-forget event frames from emitters over a Unix-domain socket.
|
Command obsigna-daemon runs the receipts daemon: a single OS-user process that owns the Ed25519 signing key and the SQLite receipt store, and receives fire-and-forget event frames from emitters over a Unix-domain socket. |
|
internal
|
|
|
anchor
Package anchor defines the external-witness sink the daemon writes rotation (and, in a later phase, checkpoint) events to (ADR-0015).
|
Package anchor defines the external-witness sink the daemon writes rotation (and, in a later phase, checkpoint) events to (ADR-0015). |
|
binresolve
Package binresolve locates sibling binaries that ship together.
|
Package binresolve locates sibling binaries that ship together. |
|
chain
Package chain owns the daemon's in-memory chain state — the next sequence number and the previous-receipt-hash for each chain id.
|
Package chain owns the daemon's in-memory chain state — the next sequence number and the previous-receipt-hash for each chain id. |
|
doctorcli
Package doctorcli implements the `agent-receipts doctor` subcommand: an end-to-end health check of the receipts pipeline (emitter → socket → daemon → SQLite → verify) described by ADR-0010.
|
Package doctorcli implements the `agent-receipts doctor` subcommand: an end-to-end health check of the receipts pipeline (emitter → socket → daemon → SQLite → verify) described by ADR-0010. |
|
keyscli
Package keyscli implements the `obsigna keys` subcommands — generate, pubkey, and rotate — that manage the daemon's Ed25519 signing key from the read-side CLI.
|
Package keyscli implements the `obsigna keys` subcommands — generate, pubkey, and rotate — that manage the daemon's Ed25519 signing key from the read-side CLI. |
|
keysource
Package keysource defines the interface the daemon uses to sign receipts.
|
Package keysource defines the interface the daemon uses to sign receipts. |
|
listcli
Package listcli implements the `agent-receipts list` subcommand: query recent receipts from a daemon-written SQLite store and print them in tabular or JSON form.
|
Package listcli implements the `agent-receipts list` subcommand: query recent receipts from a daemon-written SQLite store and print them in tabular or JSON form. |
|
pipeline
Package pipeline maps an emitter frame plus an OS-attested peer credential into a signed AgentReceipt and persists it to the store.
|
Package pipeline maps an emitter frame plus an OS-attested peer credential into a signed AgentReceipt and persists it to the store. |
|
showcli
Package showcli implements the `agent-receipts show <seq>` subcommand: inspect a single receipt by its chain sequence number, read from a daemon-written SQLite store.
|
Package showcli implements the `agent-receipts show <seq>` subcommand: inspect a single receipt by its chain sequence number, read from a daemon-written SQLite store. |
|
socket
Package socket owns the daemon's Unix-domain-socket listener, the per-OS peer-credential capture, and the length-prefix message framing.
|
Package socket owns the daemon's Unix-domain-socket listener, the per-OS peer-credential capture, and the length-prefix message framing. |
|
sockettest
Package sockettest provides shared helpers for AF_UNIX socket tests.
|
Package sockettest provides shared helpers for AF_UNIX socket tests. |
|
verifycli
Package verifycli implements the `agent-receipts verify` subcommand: validate a stored chain's signatures and hash links using a daemon-written SQLite store and the daemon-published public key.
|
Package verifycli implements the `agent-receipts verify` subcommand: validate a stored chain's signatures and hash links using a daemon-written SQLite store and the daemon-published public key. |
|
verifyeventcli
Package verifyeventcli implements the `agent-receipts verify-event` subcommand: end-to-end pipeline-provenance evidence for a single historical receipt.
|
Package verifyeventcli implements the `agent-receipts verify-event` subcommand: end-to-end pipeline-provenance evidence for a single historical receipt. |