Skip to content

Security

Threat model

The go-sequent threat model: assets, trust boundaries, threats with mitigations, explicit non-goals, and a review checklist for applications built on the library.

Threat model

This is the production-hardening threat model for go-sequent, revised 2026-10-26. It follows the locked decisions: hash-chain integrity with no event signatures, tombstone-only GDPR, and a pure library with no HTTP surface.

Assets

Asset Notes
Event store (events, commands) The source of truth. Append-only in normal operation.
Unique keys and idempotency keys Correctness under retries
Projection tables Disposable and rebuildable
Outbox An at-least-once delivery buffer
Snapshots A performance cache, stored as versioned JSON
PII in payloads Encrypt through an application-supplied PayloadProtector. GDPR erasure is by tombstone only.

Trust boundaries

  1. Client to application. Authentication happens in your application. go-sequent sees Meta.AuthContext and UserID only as opaque claims. Never trust a client-set UserID without verified authentication.
  2. Application to CommandService. AuthZ hooks (security.Authorizer) run as a filter or middleware before handlers.
  3. CommandService to PostgreSQL. A single-writer primary. Events carry a hash chain, and you add database ACLs and backups. There are no event signatures.
  4. SyncInTxn projectors. They run in the same write transaction as the append, for ledgers.
  5. Outbox pollers. Delivery is at-least-once, so handlers must be idempotent.

Threats and mitigations

Threat Mitigation
API replay or double submit Tenant-scoped idempotency keys and defensive handlers
Privilege escalation through forged commands AuthZ middleware. Use DenyByDefault for sensitive command types.
Event store tampering Hash-chain verification, plus database controls and optionally WORM storage. No signatures.
Cross-tenant access tenant_id on meta and rows, tenancy.MatchAggregateTenant, tenancy.UniqueScope
PII in logs and traces Redaction, PayloadProtector, never log secrets
Projector SQL injection Use parameterized persistors only
Workflow or outbox double execution Idempotent commands, plus outbox ack and deduplication
Snapshot poisoning Versioned JSON. Regenerate from events.
GDPR erasure Tombstone events and a projector scrub. Crypto-shredding is not a product feature.
Ruby Sequent dump import Never

A note on the hash chain

The hash chain detects modification of stored events. It does not stop someone with write access from rewriting the whole chain from the point of change. That is why you pair it with database access control, restricted restore rights and backups, and WORM storage if your regulator asks for it. See Event store.

A note on ordering

An idempotency replay is answered before the AuthZ filter runs, in the current middleware order. If a replayed command must be re-authorized, wrap authorization as middleware that runs before idempotency. See AuthZ and Idempotency.

Explicit non-goals

  • Event signatures. The hash chain is the in-framework integrity mechanism.
  • Crypto-shredding as the GDPR path.
  • A gRPC or HTTP server in this repository.

Review checklist for applications

  • An authorizer is installed for every mutating command type.
  • The tenant is always set on commands, and partition keys are tenant-scoped.
  • Money fields use types.Money, never float64.
  • SyncInTxn handles ledger balances. The outbox is only for asynchronous fan-out.
  • A backup restore drill has been completed. See Backup and restore.
  • Upcasters are registered for any renamed event payloads.

Docs track the go-sequent library. When APIs drift, the source wins.