hub-core/docs/statehub-inbox-read-pilot.md
tegwick 6fb5ce285c
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / pytest-smoke (push) Failing after 2s
feat: add guarded State Hub inbox read projection pilot
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06ed7-828d-7ca0-a8d4-0c3e5a0c4102
2026-09-05 10:37:09 +02:00

67 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# State Hub inbox read pilot
`GET /ports/projections/statehub-inbox` is an opt-in, authenticated read-only
projection over the existing `agent_messages` model. It is distinct from
`/ports/messaging/messages`, which accepts new message envelopes and does not
implement State Hub's read/archive lifecycle.
Enable only after validating and importing a consistent source snapshot:
`HUB_CORE_STATEHUB_INBOX_READS=1`, `HUB_CORE_STATEHUB_INBOX_AGENT=state-hub`.
PostgreSQL and the configured operator bearer token are required. Disabled by
default. No POST/PATCH/DELETE exists on the pilot endpoint. Imported history
never emits messages or notifications.
The response is the State Hub `MessageRead` list shape. Parameters are
`to_agent` (must match the configured pilot agent), exact `from_agent`,
`unread_only` and `limit` (11000, default 50). Broadcast messages are included;
archived messages are excluded. Results order by created_at descending, then ID
for ties. Query transactions are read-only. Responses carry `Cache-Control:
no-store` and `X-Hub-Core-Read-Mode: snapshot-pilot`.
This proves one literal agent inbox. It does not claim repository rename/alias
resolution, unscoped fleet reads, thread lookup, continuous freshness, or writer
cutover. The importer preserves thread roots so later history work need not
reconstruct dangling identities.
## Snapshot and import
Capture messages with one source PostgreSQL read-only, repeatable-read
transaction. Preserve id, sender, recipient, subject, body, thread_id, read_at,
archived_at and created_at, including archived/thread-root records. Keep the
raw snapshot in private temporary storage; commit only hashes and counts.
The bundle has schema `hub-core.statehub-inbox-snapshot.v1`, `source`,
`captured_at`, `messages`, `count`, and `content_hash`. Use `snapshot_hash` from
`hub_core.runtime.inbox_projection` for canonical UTC/UUID normalization.
`import_snapshot(session, bundle, expected_hash=..., apply=False)` validates;
`apply=True` imports under a caller-owned transaction. The caller must commit
only on success and roll back any exception. PostgreSQL apply locks the target
message table before inspecting it. Different existing content refuses all
changes; exact repeats are no-ops. Thread roots insert before replies. Duplicate
IDs, malformed rows, hash/count mismatch, absent provenance, missing roots and
cycles are rejected.
Use the existing workload database credential delivery inside the receiving
pod; never export database credentials into evidence. No schema migration or
new secret is required. This is a one-time migration helper, not a message
sending API or an automated synchronization loop.
## Pilot sequence and rollback
1. Keep State Hub as sole message writer; capture source revision, instance,
repeatable-read snapshot, data hash and expected filtered inbox results.
2. Import only into an empty receiver, verify all row hashes/counts and replay
as a no-op. Deploy the disabled reader image, then enable the private pilot.
3. Compare the same frozen source rows with authenticated receiver responses:
all/unread/sender/limit cases, broadcast inclusion and archive exclusion.
Check unauthenticated rejection and absence of writes. A later live State
Hub read is a freshness observation, not the original snapshot oracle.
4. Disable the reader flag and verify the endpoint returns 404; re-enable and
recheck the same hash. Retain the imported rows for review; do not delete
history as a rollback technique. Runtime rollback uses the previous image.
Transport from State Hub must be explicitly admitted by rapp-core-hub's
NetworkPolicy. Scope any pilot exception to State Hub API pod labels and the
candidate port. No public Ingress or production message-writer change is part
of this pilot. The operator bearer stays in its existing credential lane;
production caller credentials and ongoing freshness require their own cutover.