# Auth / Session Service Implementation Plan This plan has been already implemented and stays here for historical reasons. It should NOT be threated as source of truth for service functionality. ## Purpose This plan describes a detailed, incremental implementation path for [`Auth / Session Service`](README.md) that integrates with the existing `Edge Gateway`. The plan is intentionally atomic. Each stage should be small enough to implement, review, and test without overloading development context. ## Global Rules for the Entire Plan - keep domain logic independent from concrete storage backends; - keep gateway projection separate from source-of-truth records; - preserve the existing public auth contract expected by gateway: - `send-email-code` -> `challenge_id` - `confirm-email-code` -> `device_session_id` - keep `confirm-email-code` synchronous; - do not introduce a pending async session-provisioning model; - use synchronous internal REST where immediate answer is required; - use Redis Streams / pub-sub only for session lifecycle propagation and other event-style side effects; - keep implementation idempotent where retries are expected; - design Redis-backed stores behind interfaces so SQL migration remains possible. ## Milestone Structure Suggested milestones: 1. Domain skeleton and ports 2. In-memory service behavior and tests 3. Redis-backed source-of-truth stores 4. Gateway projection publisher 5. Public HTTP API 6. Internal trusted API 7. Integration with user-service and config-provider ports 8. Revoke/block flows 9. Observability and hardening 10. End-to-end integration with gateway ## Suggested Module Structure The structure described below is allowed to be changed during the Plan steps implementation. ```text authsession/ ├── cmd/ │ └── authsession/ │ └── main.go │ ├── internal/ │ ├── app/ │ │ ├── app.go │ │ ├── bootstrap.go │ │ └── wiring.go │ │ │ ├── config/ │ │ ├── config.go │ │ ├── env.go │ │ └── validation.go │ │ │ ├── domain/ │ │ ├── challenge/ │ │ │ ├── model.go │ │ │ ├── state.go │ │ │ ├── policy.go │ │ │ └── errors.go │ │ │ │ │ ├── devicesession/ │ │ │ ├── model.go │ │ │ ├── state.go │ │ │ ├── revoke.go │ │ │ └── errors.go │ │ │ │ │ ├── userresolution/ │ │ │ ├── model.go │ │ │ └── policy.go │ │ │ │ │ ├── sessionlimit/ │ │ │ ├── model.go │ │ │ └── policy.go │ │ │ │ │ └── common/ │ │ ├── email.go │ │ ├── time.go │ │ ├── ids.go │ │ └── types.go │ │ │ ├── ports/ │ │ ├── challengestore.go │ │ ├── sessionstore.go │ │ ├── userdirectory.go │ │ ├── configprovider.go │ │ ├── mailsender.go │ │ ├── projectionpublisher.go │ │ ├── clock.go │ │ ├── idgenerator.go │ │ ├── codegenerator.go │ │ └── codehasher.go │ │ │ ├── service/ │ │ ├── sendemailcode/ │ │ │ └── service.go │ │ ├── confirmemailcode/ │ │ │ └── service.go │ │ ├── getsession/ │ │ │ └── service.go │ │ ├── listusersessions/ │ │ │ └── service.go │ │ ├── revokedevicesession/ │ │ │ └── service.go │ │ ├── revokeallusersessions/ │ │ │ └── service.go │ │ ├── blockuser/ │ │ │ └── service.go │ │ └── shared/ │ │ ├── normalize.go │ │ ├── projection.go │ │ └── publicerrors.go │ │ │ ├── api/ │ │ ├── publichttp/ │ │ │ ├── handler_send_email_code.go │ │ │ ├── handler_confirm_email_code.go │ │ │ ├── dto.go │ │ │ └── errors.go │ │ │ │ │ └── internalhttp/ │ │ ├── handler_get_session.go │ │ ├── handler_list_user_sessions.go │ │ ├── handler_revoke_device_session.go │ │ ├── handler_revoke_all_user_sessions.go │ │ ├── handler_block_user.go │ │ ├── dto.go │ │ └── errors.go │ │ │ ├── adapters/ │ │ ├── redis/ │ │ │ ├── challengestore/ │ │ │ │ └── store.go │ │ │ ├── sessionstore/ │ │ │ │ └── store.go │ │ │ ├── configprovider/ │ │ │ │ └── provider.go │ │ │ └── gatewayprojection/ │ │ │ ├── publisher.go │ │ │ ├── snapshot.go │ │ │ └── stream.go │ │ │ │ │ ├── userservice/ │ │ │ ├── client.go │ │ │ ├── mapper.go │ │ │ └── stub.go │ │ │ │ │ ├── mail/ │ │ │ ├── stub.go │ │ │ └── rest_client.go │ │ │ │ │ ├── crypto/ │ │ │ ├── codehasher.go │ │ │ └── publickey.go │ │ │ │ │ ├── clock/ │ │ │ └── system.go │ │ │ │ │ └── id/ │ │ ├── challengeid.go │ │ └── devicesessionid.go │ │ │ ├── observability/ │ │ ├── logging.go │ │ ├── metrics.go │ │ └── tracing.go │ │ │ └── testkit/ │ ├── fixtures.go │ ├── fake_clock.go │ ├── fake_idgen.go │ ├── fake_mail.go │ ├── fake_userdir.go │ └── fake_projection.go │ ├── api/ │ ├── public-openapi.yaml │ └── internal-openapi.yaml │ └── README.md ``` ### Description - `cmd/authsession` — service entry point: process startup, configuration loading, application assembly, and HTTP server startup. - `internal/app` — top-level application orchestration layer: dependency initialization, runtime bootstrap, and component wiring. - `internal/config` — service configuration loading, normalization, and validation from environment and other sources. - `internal/domain/challenge` — domain model for the `send_email_code` / `confirm_email_code` challenge flow: states, transitions, TTL/retry policies, and domain errors. - `internal/domain/devicesession` — domain model for `device_session`: session state, revocation, revoke reasons, and related domain errors. - `internal/domain/userresolution` — domain model for user resolution by email through user-service: existing user, allowed registration, or blocked user. - `internal/domain/sessionlimit` — domain model and policy rules for active `device_session` limits. - `internal/domain/common` — shared domain value objects and helper types: email, time, identifiers, and common primitive types. - `internal/ports` — interfaces for all external dependencies: source-of-truth stores, user-service, mail delivery, config, projection publisher, clock, generators, and hashing. - `internal/service/sendemailcode` — use case for sending a code: email normalization, challenge lifecycle, suppression/send decision, and success-shaped public response. - `internal/service/confirmemailcode` — use case for confirming a code: challenge validation, public-key validation, resolve/create user flow, session-limit enforcement, `device_session` creation, and projection publication. - `internal/service/getsession` — use case for reading a single `device_session` for the trusted internal API. - `internal/service/listusersessions` — use case for listing user sessions for the trusted internal API. - `internal/service/revokedevicesession` — use case for revoking a single device session and publishing the updated gateway projection. - `internal/service/revokeallusersessions` — use case for revoking all active sessions of a user and publishing the resulting updates. - `internal/service/blockuser` — use case for blocking a user/email and revoking active sessions according to policy. - `internal/service/shared` — shared application-layer code: normalization helpers, gateway projection builders, and public error mapping. - `internal/api/publichttp` — public HTTP API for gateway integration: handlers, DTOs, and error mapping for `send_email_code` and `confirm_email_code`. - `internal/api/internalhttp` — trusted internal HTTP API: revoke/read/list/block endpoints, DTOs, and separate internal error policy. - `internal/adapters/redis/challengestore` — Redis adapter for source-of-truth challenge storage. - `internal/adapters/redis/sessionstore` — Redis adapter for source-of-truth `device_session` storage. - `internal/adapters/redis/configprovider` — Redis adapter for dynamic configuration, such as active-session limits. - `internal/adapters/redis/gatewayprojection` — Redis adapter for the `Edge Gateway` integration projection: KV snapshots and lifecycle updates in streams. - `internal/adapters/userservice` — user-service integration adapter: REST client, response-to-domain mapping, and stub implementation for early stages. - `internal/adapters/mail` — mail-delivery adapter: development stub and future REST mail-service client. - `internal/adapters/crypto` — cryptographic adapters: confirmation-code hashing and `client_public_key` validation/parsing. - `internal/adapters/clock` — system clock implementation. - `internal/adapters/id` — generation of stable domain identifiers such as `challenge_id` and `device_session_id`. - `internal/observability` — service logging, metrics, and tracing. - `internal/testkit` — test fixtures, fake/mock dependencies, and shared helpers for unit and integration tests. - `api/public-openapi.yaml` — formal specification of the public HTTP API. - `api/internal-openapi.yaml` — formal specification of the trusted internal HTTP API. - `README.md` — architectural service description covering its role in the system, contracts, domain rules, and integrations. --- ## ~~Stage 1.~~ Freeze the Service Contract Status: implemented. ### Goal Write down the exact service-level contracts before implementation starts. ### Tasks - freeze public auth use cases: - `send_email_code` - `confirm_email_code` - freeze internal trusted use cases: - `GetSession` - `ListUserSessions` - `RevokeDeviceSession` - `RevokeAllUserSessions` - `BlockUser` - define canonical request/response DTOs for the service boundary; - define client-safe error classes for the public auth API; - define richer internal error classes for logs and internal API. ### Deliverables - service contract notes in repo docs; - initial error catalog; - agreement on public vs internal API boundaries. ### Exit Criteria - no unresolved ambiguity around public auth input/output shapes; - no unresolved ambiguity around internal revoke/read operations. --- ## ~~Stage 2.~~ Define Core Domain Types Status: implemented. ### Goal Create the minimal domain model without any transport or storage code. ### Tasks - define challenge aggregate concept; - define device-session aggregate concept; - define revoke reason model; - define user resolution result model: - existing user - creatable user - blocked user - define session-limit decision model; - define mail-delivery result model; - define projection snapshot model for gateway integration; - define domain statuses and allowed transitions. ### Important Constraints - challenge and session models must not depend on Redis-specific encoding; - gateway projection model must be separate from domain entities. ### Deliverables - domain package with types only; - transition invariants documented in code comments and tests. ### Exit Criteria - domain package compiles without storage adapters; - status transitions are covered by unit tests. --- ## ~~Stage 3.~~ Define Service Ports Status: implemented. ### Goal Create clean interfaces around every external dependency. ### Tasks Define interfaces conceptually equivalent to: - `ChallengeStore` - `SessionStore` - `UserDirectory` / `UserResolver` - `ConfigProvider` - `MailSender` - `GatewaySessionProjectionPublisher` - `Clock` - `IDGenerator` - `CodeGenerator` - `CodeHasher` ### Notes - `ChallengeStore` and `SessionStore` are source-of-truth ports; - `GatewaySessionProjectionPublisher` is an integration port, not a domain store; - `UserDirectory` must support existing / creatable / blocked decisions and user creation when allowed; - `ConfigProvider` must support "limit absent" as a first-class case. ### Deliverables - interface package or packages; - port-level test doubles. ### Exit Criteria - service layer can be implemented against interfaces only. --- ## ~~Stage 4.~~ Implement Pure Domain Services In Memory Status: implemented. ### Goal Implement the auth logic once, against in-memory stores and adapters. ### Tasks Implement core use cases: - `SendEmailCode` - `ConfirmEmailCode` - `GetSession` - `ListUserSessions` - `RevokeDeviceSession` - `RevokeAllUserSessions` - `BlockUser` ### Required Behaviors #### SendEmailCode - normalize email; - consult `UserDirectory` policy if needed; - create challenge; - generate secure code; - store only hashed code; - attempt delivery or suppress it; - always return a success-shaped result with `challenge_id`. #### ConfirmEmailCode - load challenge; - validate expiration and status; - validate code hash; - validate `client_public_key` format; - handle idempotent repeat confirm for same successful challenge and same key; - resolve/create user through `UserDirectory`; - reject blocked user; - load session-limit config; - count active sessions; - reject if limit exceeded; - create session; - store session; - move challenge into short-window confirmed state; - publish session projection; - return `device_session_id`. #### Revoke Flows - update source of truth; - publish revoked projection for every affected session. ### Deliverables - service layer with in-memory dependencies; - unit tests for every public behavior. ### Exit Criteria - full service logic is testable without Redis or HTTP; - edge cases are covered by unit tests. --- ## ~~Stage 5.~~ Design Challenge Rules in Detail Status: implemented. ### Goal Remove ambiguity from challenge handling before persistent adapters are written. ### Tasks - define challenge TTL; - define max confirm attempts; - define resend behavior policy, if any; - define short idempotency window after successful confirm; - define state machine for: - new challenge - sent/suppressed - confirmed - expired - failed - define exact behavior for repeated confirms: - same code + same key -> same session id - same code + different key -> fail - expired challenge -> fail - too many attempts -> fail ### Deliverables - explicit challenge policy spec in code comments/tests. ### Exit Criteria - no hidden challenge behavior remains undecided. --- ## ~~Stage 6.~~ Define Public Error Policy Status: implemented. ### Goal Make public auth failures predictable and safe. ### Tasks Decide exact client-safe categories for: - malformed e-mail; - malformed `client_public_key`; - unknown challenge; - expired challenge; - invalid code; - blocked by policy at confirm stage; - session limit exceeded; - temporarily unavailable. ### Additional Rules - `send_email_code` must not reveal whether the e-mail exists or is blocked; - public errors should be normalized for gateway passthrough; - internal logs and traces may keep richer reasons. ### Deliverables - public error mapping table; - internal error hierarchy. ### Exit Criteria - gateway adapter behavior can be implemented without guesswork. --- ## ~~Stage 7.~~ Implement Redis ChallengeStore Status: implemented. ### Goal Add the first persistent backend for challenges. ### Tasks - implement challenge read/write/update operations in Redis KV; - define Redis key scheme for challenges; - store hashed codes only; - store challenge status and timestamps; - support atomic compare-and-set style updates where required; - support expiration cleanup through TTL and/or explicit status. ### Important Design Rule The interface must not expose Redis primitives directly. ### Deliverables - Redis-backed challenge store adapter; - adapter integration tests against Redis. ### Exit Criteria - challenge lifecycle works against Redis under concurrent access assumptions. --- ## ~~Stage 8.~~ Implement Redis SessionStore Status: implemented. ### Goal Add the first persistent backend for sessions. ### Tasks - implement create/read/list/revoke operations; - define Redis key scheme for sessions; - support listing all sessions for one user; - support revoking one session; - support revoking all sessions for one user; - support block-related session revocation; - support active-session counting for limit enforcement; - store revoke reason and actor metadata. ### Important Design Rule The session source-of-truth record must remain distinct from gateway projection encoding. ### Deliverables - Redis-backed session store adapter; - adapter integration tests. ### Exit Criteria - all session lifecycle operations are persistent and testable. --- ## ~~Stage 9.~~ Implement Redis ConfigProvider Status: implemented. ### Goal Support dynamic session-limit configuration. ### Tasks - implement config lookup from Redis KV; - define config key scheme for auth-service settings; - support: - limit present with integer value - limit absent - invalid config value - define fallback behavior for invalid config read. ### Required Behavior - missing config -> no session-count limit; - invalid config -> fail closed or fail safe according to explicit decision; - document the chosen policy. ### Deliverables - Redis-backed config adapter; - tests for absent, valid, and invalid values. ### Exit Criteria - session-limit logic no longer depends on hard-coded constants. --- ## ~~Stage 10.~~ Implement Gateway Session Projection Publisher Status: implemented. ### Goal Bridge auth source-of-truth state into gateway-facing cache/projection state. ### Tasks - define exact projection snapshot structure consumed by gateway; - define Redis KV key scheme for gateway session lookup; - define Redis Stream schema for session lifecycle updates; - implement projection write on session create; - implement projection update on session revoke; - implement projection update for bulk revoke/all; - make publication idempotent and retry-safe. ### Important Constraints - projection publisher should accept domain session data and transform it; - it must not force domain logic to know Redis snapshot shape. ### Deliverables - Redis-backed projection publisher; - integration tests that emulate gateway expectations. ### Exit Criteria - created sessions appear in gateway-readable projection; - revoked sessions produce gateway-readable invalidation/update records. --- ## ~~Stage 11.~~ Implement Stub MailSender Status: implemented. ### Goal Introduce the mail-delivery port without coupling auth logic to one concrete delivery transport. ### Tasks - create a stub adapter with deterministic success/failure modes; - record delivery attempts for tests; - support explicit suppression mode for blocked/hidden flows; - ensure service logic can distinguish: - sent - suppressed - failed ### Deliverables - stub mail adapter; - tests around challenge delivery state transitions. ### Exit Criteria - auth logic is fully testable without real mail infrastructure. --- ## ~~Stage 12.~~ Implement Stub UserDirectory Status: implemented. ### Goal Introduce the user-service dependency before its real service exists. ### Tasks - create an in-memory or stub REST-like adapter that can return: - existing user - creatable user - blocked user - support create-on-confirm behavior; - support lookups by normalized email; - support user block state. ### Deliverables - stub user-service adapter; - integration tests for auth flows. ### Exit Criteria - auth-service no longer needs to fake user decisions internally. --- ## ~~Stage 13.~~ Implement Public HTTP API Status: implemented. ### Goal Expose the synchronous public auth flow expected by gateway. ### Tasks - create HTTP handlers for: - `send_email_code` - `confirm_email_code` - define JSON DTOs matching gateway expectations; - implement request validation; - implement response normalization; - implement mapping from internal errors to public client-safe errors; - add request timeout handling and structured logging. ### Important Constraints - keep semantics aligned with gateway adapter expectations; - do not expose internal admin/session methods on the public listener. ### Deliverables - public HTTP server; - handler tests; - end-to-end tests through HTTP. ### Exit Criteria - gateway can call the service through a real HTTP adapter. --- ## ~~Stage 14.~~ Implement Internal Trusted API Status: implemented. ### Goal Expose lifecycle and read operations for trusted internal callers. ### Tasks Implement internal endpoints for: - `GetSession` - `ListUserSessions` - `RevokeDeviceSession` - `RevokeAllUserSessions` - `BlockUser` Optional additions later: - unblock flow; - challenge inspection. ### Notes - this may use REST for simplicity; - authentication/authorization of internal callers can be stubbed initially if there is not yet a platform-wide internal auth mechanism. ### Deliverables - internal HTTP API; - handler tests. ### Exit Criteria - session lifecycle can be driven without touching Redis manually. --- ## ~~Stage 15.~~ Implement Revoke Logic Thoroughly Status: implemented. ### Goal Make revoke behavior explicit and reliable. ### Tasks For `RevokeDeviceSession`: - load target session; - no-op or explicit result if already revoked; - persist revoke metadata; - publish revoked projection. For `RevokeAllUserSessions`: - list active sessions for user; - revoke each relevant session; - publish projection for each affected session; - preserve reason metadata. For `BlockUser`: - mark user blocked through `UserDirectory` or trusted policy adapter; - revoke all active sessions; - ensure future auth flow is denied at confirm stage and mail can be suppressed at send stage. ### Deliverables - complete revoke implementation; - tests for single, bulk, and block flows. ### Exit Criteria - gateway-facing revoke propagation is available for all revoke models. --- ## ~~Stage 16.~~ Add Consistency Safeguards Status: implemented. ### Goal Reduce create/revoke drift between source of truth and gateway projection. ### Tasks - identify all places where source-of-truth write and projection publish happen; - add retry strategy for projection writes; - make projection publication idempotent; - define recovery behavior if projection publish fails after source-of-truth success; - add dead-letter or repair strategy placeholder if needed later; - document the consistency model. ### Preferred Short-Term Outcome - source-of-truth success is never reported as auth success unless projection write/publish reached the required success threshold, or the failure handling policy is explicit and tested. ### Deliverables - consistency policy document; - tests for partial failure scenarios. ### Exit Criteria - known failure windows are explicit and bounded. --- ## ~~Stage 17.~~ Add Public Anti-Abuse Hooks Status: implemented. ### Goal Prepare the auth service for safe interaction behind gateway public routing. ### Tasks - add service-level hooks for challenge resend throttling; - add max-attempt handling per challenge; - add metrics for suppressed/blocked/sent flows; - preserve soft anti-enumeration outward behavior. ### Notes Gateway already applies public-edge rate limits. This stage is about auth-specific flow protection, not replacing gateway limits. ### Deliverables - abuse-control policy inside auth domain; - tests for throttling and attempt exhaustion. ### Exit Criteria - auth flow cannot be trivially abused through repeated confirm attempts. --- ## ~~Stage 18.~~ Add Observability Status: implemented. ### Goal Make the service operable from the beginning. ### Tasks - structured logs for all major state transitions; - metrics for all major operations; - tracing spans for public auth flow and internal API; - redact secrets and codes from logs; - include stable identifiers such as challenge id, device session id, user id, and reason codes where safe. ### Minimum Metrics - challenges created; - deliveries sent/suppressed/failed; - confirm attempts; - confirm successes/failures; - sessions created; - session limit rejections; - sessions revoked by reason; - projection publish failures; - user-resolution outcomes. ### Deliverables - metrics endpoint wiring if needed; - logging/tracing middleware; - observability tests where practical. ### Exit Criteria - production debugging is possible without adding ad hoc logs later. --- ## ~~Stage 19.~~ Add Gateway-Compatibility Tests Status: implemented. ### Goal Test auth-service not just in isolation, but against gateway expectations. ### Tasks - verify public auth HTTP DTO compatibility; - verify `confirm-email-code` returns ready `device_session_id`; - verify created session projection is readable by a gateway-compatible reader; - verify revoked projection invalidates session; - verify repeated confirm returns same session id in idempotency window; - verify blocked e-mail still keeps `send_email_code` outwardly success-shaped; - verify session limit exceeded returns stable client-visible error; - verify malformed `client_public_key` is rejected. ### Deliverables - integration test suite focused on gateway contract. ### Exit Criteria - no ambiguity remains about integration with existing gateway behavior. --- ## ~~Stage 20.~~ Add Real REST Adapter to User Service Contract Status: implemented. ### Goal Prepare for future extraction of `User Service`. ### Tasks - define internal REST client for user resolution/create/block operations; - keep stub implementation for tests; - add timeout, retry, and error mapping policy; - define normalized email rules at the boundary. ### Deliverables - REST client adapter for future user-service; - compatibility tests using stub server. ### Exit Criteria - auth-service can later switch from stub to real user-service with no domain rewrite. --- ## ~~Stage 21.~~ Add Real Mail Adapter Contract Status: implemented. ### Goal Prepare for later internal mail-service-backed delivery. ### Tasks - define mail adapter request/response contract; - preserve current stub for tests; - define delivery timeout and error mapping; - define how suppression vs explicit failure is represented. ### Deliverables - mail adapter interface finalized; - optional HTTP client adapter skeleton. ### Exit Criteria - auth flow is decoupled from the future mail implementation. --- ## ~~Stage 22.~~ Production Hardening Pass Status: implemented. ### Goal Review edge cases before calling the service implementation complete. ### Tasks - test Redis reconnect behavior; - test duplicate publish behavior; - test crash/restart around confirm and revoke flows; - test large numbers of active sessions per user; - test concurrent confirms against the same challenge; - test concurrent revoke and confirm races; - test block-user during active auth flow; - test expired challenge cleanup strategy. ### Deliverables - hardening checklist; - race-condition tests; - operational notes. ### Exit Criteria - no major known race remains undocumented. --- ## ~~Stage 23.~~ Optional Cleanup and Migration Readiness Status: implemented. ### Goal Make future SQL migration realistic. ### Tasks - review whether domain services leak Redis assumptions; - ensure all store interfaces are storage-agnostic; - isolate key naming, stream naming, and projection serialization; - add adapter contract tests reusable by future SQL backends. ### Deliverables - backend-agnostic adapter tests; - migration readiness notes. ### Exit Criteria - a future SQL backend can be added without reworking service-layer logic. --- ## Recommended First Working Slice If implementation needs an aggressively small first milestone, do this subset first: 1. domain types 2. service ports 3. in-memory service logic 4. stub `UserDirectory` 5. stub `MailSender` 6. public HTTP API 7. Redis `SessionStore` 8. Redis `ChallengeStore` 9. Redis projection publisher 10. gateway-compatibility tests for: - send-email-code - confirm-email-code - session projection after confirm This gives an end-to-end happy path quickly, without waiting for revoke/admin and full hardening. ## Recommended Second Slice 1. internal trusted API 2. session-limit config provider 3. revoke-device 4. revoke-all 5. block-user 6. observability 7. consistency safeguards 8. hardening tests ## Final Acceptance Criteria The service can be considered implementation-ready when all of the following are true: - gateway can call public auth routes synchronously; - `confirm-email-code` returns a ready `device_session_id`; - the created session appears in gateway-compatible projection storage; - revoked sessions publish gateway-compatible revoke updates; - repeated successful confirm returns the same session id during the short idempotency window; - session creation respects dynamic limit config; - user block prevents future auth flow and can revoke active sessions; - all storage is hidden behind interfaces; - auth-service is not required on the authenticated command hot path; - logs, metrics, and tests cover the full lifecycle. ## Implementation Order Summary ```mermaid flowchart TD A["Freeze contracts"] B["Domain model"] C["Ports"] D["In-memory service logic"] E["Redis stores"] F["Projection publisher"] G["Public HTTP API"] H["Internal trusted API"] I["Revoke and block flows"] J["Observability and hardening"] K["Gateway compatibility tests"] A --> B --> C --> D --> E --> F --> G --> H --> I --> J --> K ```