Runtime and memory (code map)
The repo guide splits runtime orientation into two major tracks:
| Area | Primary Files |
|---|---|
| Steelmoth runtime behavior | steelmoth-runtime/src/steelmoth_runtime/runtime.py, steelmoth-runtime/src/steelmoth_runtime/memlink.py |
| Memlink service behavior | memlink-hybrid/README.md, memlink-hybrid/openapi.yaml, memlink-hybrid/src/memlink_hybrid/ |
steelmoth-runtime is the active conversation runtime package. The root
README identifies it as responsible for Telegram ingress, reply generation,
runtime context, and retrieval integration.
Account-runtime channel unification (Phases 1-8, committed) consolidated the
normal-chat reply path through process_account_turn so Web UI and Telegram
share one planner, tool registry, memory boundary, and reply pipeline. Phase 9
/ 9-bis / 10 are in flight per PRD 0043 + RFC 0080 to close the remaining
operational gaps: Phase 9 (ADR 0121) turns the Telegram relay route from a
parallel event consumer into an event forwarder that publishes through the
same SSE persistence + broadcast pipeline the dashboard uses, so Telegram-
originated turns mirror live into Web UI. Phase 9-bis fixes the memlink 400
on out:telegram:* assistant ingest. Phase 10 (ADR 0120 + ADR 0122) retires
the telegram_photo:<path> text marker and introduces a structured channel-
neutral Attachment type with an owner-scoped store and scope-authed
retrieval route.
Runtime-owned memory boundaries now include canonical owner identity handling
for web-chat living-document routes, client-editable owner-scoped
user_model_summary surfaced through the client-facing learning-profile
alias, and the operator-only agent_system_prompt living-document override
consumed by chat prompt assembly. Use runtime.md, data-and-scope.md, and
web-chat-api.md for the detailed auth, owner-scope, and fallback behavior
before editing routes_memory.py, dashboard memory components, runtime
owner-id resolution, or prompt assembly.
Runtime-owned self-code source inspection uses source_search.py for
read-only term extraction and ranking, self_code.py for direct self-code
read/list/search commands, and self_code_safety.py for shared protected-path
exclusion plus snippet redaction. The read path and search path share one
post-filter classifier so nested protected directories and key/cert variants
are blocked consistently. Use runtime.md for the detailed source-answer
synthesis and fallback behavior.
memlink-hybrid is the memory service. The root README identifies it as a
standalone service backed by PostgreSQL schemas for core, world, and moth
memory. The OpenAPI contract is memlink-hybrid/openapi.yaml.
It serves HTTP concurrently: infrastructure/server.py runs a stdlib
ThreadingWSGIServer (one request per daemon thread) and
adapters/outbound/db.py (PostgresDatabase) is backed by a bounded
psycopg_pool.ConnectionPool — each request thread and the single in-process
derivation worker check out their own connection (ADR 0146 Phase 1, which
replaced the prior single-threaded wsgiref server + one RLock-guarded
connection that serialized all callers — the garcia chat-history outage).
World-memory RLS (migration 0079) stays correct under pooling because the
app.owner_id scope is a transaction-local GUC set per checkout; the
recycled-connection isolation guarantee is enforced by
tests/test_pool_rls_isolation.py.
Inbound auth (adapters/inbound/auth.py) carries an Ed25519 local public-key
verify path alongside the legacy HMAC scheme (ADR 0146). The prontera prerequisite
that feeds it — per-tenant Ed25519 keypairs, an Ed25519 /sign returning
{signature, kid}, and a GET /.well-known/memlink-jwks.json carrying the
owner↔kid binding — is specified in
docs/plans/2026-06-17-memlink-phase2-prontera-ed25519-sign-jwks-spec.md
(Phase 2, operator-gated; deployed dormant on the prontera broker).
Phase 3 / Design B wires the verifier’s two resolvers (public_key_resolver /
owner_kid_allowlist) as a composition of two pieces (bootstrap):
infrastructure/jwks_key_store.py (JwksKeyStore) is a pure per-vault
kid → public key cache (lazy, fail-closed, refresh-on-unknown-kid
debounced/rate-limited/circuit-broken, bounded stale-on-error), and
adapters/outbound/owner_vault_directory.py (OwnerVaultDirectory) resolves
owner → currently-assigned vault from the client-provisioning control-plane
tables (steelmoth.clients ⋈ pool_slots, memlink’s own Postgres; short TTL +
negative-cache + rate-limit so an auth-failure flood can’t exhaust the pool; the
operator is a static entry). So a request claiming owner A is verified against A’s
assigned vault’s key — and because clients.owner_id is the single source feeding
both the gateway’s signing identity and this lookup, the binding can’t drift (ADR
0146 #F; the JWKS carries no owner_id). It stays inert — the resolvers
are built only when MEMLINK_HYBRID_SCOPE_AUTH_ASYMMETRIC is on AND a JWKS store +
Postgres store exist (else None), so a default-off build does zero network I/O and
the verify path is byte-identical until activation (Phase 4/5, gated). The
runtime-signer↔verifier canonical bytes are pinned equal by a shared golden corpus
(steelmoth-runtime + memlink-hybrid tests).
The enforce precondition is rotation-on-release (ADR 0146, operator-decided
Option A — retire-don’t-reuse): a pool slot reused across owners would let a prior
owner’s retained (un-rotatable) token sign for the slot’s next owner once verify is
enforced. The provisioner’s release path retires the slot (status='retired',
never reused) instead of freeing it when ProvisionerConfig.retire_slot_on_release
(env STEELMOTH_PROVISIONER_RETIRE_SLOT_ON_RELEASE) is set — default-off/inert; flip
near enforce. Design + decision:
docs/plans/2026-06-17-memlink-rotation-on-release-design.md.
Maintenance Notes
Section titled “Maintenance Notes”- Update this page when runtime wiring, memlink API contracts, or the repo guide’s runtime reading order changes.
- For endpoint or schema changes, use the OpenAPI contract and relevant handler code as sources before editing this page.
- For live runtime fixes, follow the engineering pre-flight rule: prove the edited file and function are the live path before changing code.
Known Unknowns
Section titled “Known Unknowns”- This page is a high-level orientation only. It does not enumerate all runtime routes, adapters, migrations, or sidecar integrations.
- It does not assert the current production container image or environment.

