Skip to content

Ports

The contract each port keeps and every adapter must meet. For the shape, read ports and adapters. Logical keys are in keys, adapter specifics in adapter details, the adapter list in adapters, build status in capabilities.

Go interfaces are in internal/port, the suite in internal/port/porttest. internal/store builds the set from ports.adapter.

Secrets

port.Secrets: whole values under slash-separated paths, each with a version. A value is at most MaxSecret (8 KiB). The suite is porttest.RunSecrets. Decided in ADR 0041.

Operation Meaning Errors
Get(path) value and version ErrNotFound
Put(path, value) write unconditionally
PutIfVersion(path, value, version) write if the version is unchanged; empty version means only if absent ErrConflict, ErrNotFound
Delete(path) remove
List(prefix) names by whole segments, never values

State

Key-value records with a lifetime and a revision, no relations. Keys look like a.b.c; a prefix ends at a .. A value is at most 256 KiB; larger content is a blob.

Operation Meaning Errors
Get(key) value and revision ErrNotFound if absent or expired
Put(key, value, ttl) write unconditionally ErrTooLarge
Create(key, value, ttl) write if absent; expired counts as absent ErrExists
Update(key, value, ttl, rev) compare-and-swap on the revision ErrConflict, ErrNotFound
Delete(key) remove; no error if absent
DeleteIfRevision(key, rev) remove if the revision is unchanged ErrConflict, ErrNotFound
List(prefix, page) live records in key order, paged ErrBadPage
Watch(prefix) put, delete and expiry events from now stream ends with the adapter error
Rule Meaning
Single key No multi-key transactions or secondary indexes
TTL Required on Put, Create, Update unless keys marks the record permanent (0)
Revision Opaque; changes on every write; compare for equality only
Expiry Get and List filter on the caller's clock, whatever the engine has swept
Paging A token continues from a key; no record appears twice
Watch At-least-once, unordered across keys, carries key and revision; reconcile with List after a reconnect
Other errors Mapped to ErrUnavailable; sign-in refuses the request

A lease is Create of lease.<target> with a short TTL, renewed by Update, released by DeleteIfRevision. A holder that fails to renew stops before its next external write. Code uses rails.Leases (Acquire, Renew, Release, Do).

Optional capabilities:

Capability Meaning
StateExporter, IndexExporter Live records or sets with remaining lifetime; used by sluis migrate
RevisionPeeker PeekRevision(key) returns an eventually consistent revision, never a value. Without it, a consistent Get serves
Replacer, ReaderAll (Blob) Replace or read every object under a prefix in one request

Index is a transitional unordered set with an expiry refreshed on Add. New features do not use it.

Blob

Whole objects read whole: target status reports and directory snapshots. Both platforms use S3 with a prefix per module (github/, slack/, google/), server-side encryption and no public access. Blob also has Delete and List(prefix).

Operation Meaning Errors
Read(name) object and version ErrNotFound
Write(name, body) replace ErrUnavailable
WriteIfVersion(name, body, version) replace if unchanged ErrConflict

Trigger

Operation Meaning
Notify(target) ask for a tick; coalesces with one waiting
Subscribe(handler) run handler(target) for each notification
Adapter Delivery
dynamodb Notify puts notify.<target> (one-minute lifetime); Subscribe polls the prefix, across replicas
invoke Notify is an asynchronous lambda:Invoke of the tick function; EventBridge Scheduler is the backstop
legacy, memory In-process only

A notification may be duplicated or lost; the lease and the backstop cover both. Writing a share. record notifies the guest's tick.

Inputs, identity and audit

Port Contract
Inputs Read-only policy, configuration file and operator secrets. Sluis holds no permission to write them
Identity Verify(token, audiences) (subject, error). Inbound: ServiceAccount or AWS federation token (connect AWS workloads, ADR 0030). Outbound: the platform's own token or role
Audit sink connect, sqs or log, from the audit concern. A sign-in is refused when its record cannot be written (design). Settings: the sqs adapter

Conformance

One suite runs against every adapter and gates adapter changes and sluis migrate. DynamoDB runs on LocalStack. An adapter that cannot pass an assertion documents the reason; a silent skip fails the suite.

Assertion Check
CAS races Of N concurrent Update or Create, one succeeds, the rest return ErrConflict or ErrExists. DeleteIfRevision loses to a newer write
TTL visibility A record is visible until expiry and never after; Create succeeds over an expired one
Lease takeover A held lease cannot be taken; an expired one is taken by one racer; a late renewal fails
Prefix paging Every live record once, in order, across pages and concurrent writes; a foreign token is refused
Revisions Every write changes the revision; a stale Update fails
Watch Put, delete and expiry are observed
Limits Oversized values give ErrTooLarge
Blobs WriteIfVersion loses to a newer write