hub-core/docs/runtime.md
tegwick f582773f5a
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / pytest-smoke (push) Failing after 1s
feat: consume Repo Manager classification publisher
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a053ff-1d6f-7fe2-ac1c-a6eb40a42a0c
2026-09-01 00:49:30 +02:00

7 KiB

Hub Core runtime

The runtime implements the packaging decision in ADR-0001, the minimal vertical in HUB-WP-0004-T04, and the durable Core Hub absorption boundary in HUB-WP-0005. Public authority still moves only through the gated deployment slices in the accepted absorption plan.

Processes

The runtime dependency extra installs one hub-core command with three process modes:

uv sync --extra runtime
hub-core api --host 127.0.0.1 --port 8010
hub-core mcp --host 127.0.0.1 --port 8011 --api-base http://127.0.0.1:8010
hub-core migrate head --database-url postgresql+asyncpg://...
hub-core migration validate core-hub-export.json
hub-core migration import core-hub-export.json --database-url postgresql+asyncpg://...
hub-core migration export --database-url postgresql+asyncpg://... --output reverse-delta.json

The migration command converts the async PostgreSQL URL for the packaged synchronous Alembic environment. Production must run migrations explicitly; the API does not auto-create tables.

Runtime ports

Port Initial path Behavior
port.registry POST /ports/registry/registrations Validates and idempotently records a descriptor/manifest package
port.messaging GET/POST /ports/messaging/messages Addressed messages with optional conversation identity
port.events.progress POST /ports/events/progress Accepts only cataloged progress-family events
port.events.interaction POST /ports/events/interaction Accepts only cataloged interaction-family events
port.projection.query GET /ports/projections/{id} Rebuildable registry/message/event projections with provenance
port.projection.query GET /ports/projections/repository-navigation/repositories Snapshot-bound cross-repository classification navigation
port.projection.query GET /ports/projections/workloads Normalized authoritative workload projection and exact resolution

Available projection ids are hub_registry, messages, progress_events, and interaction_events. The two event families use distinct stores and cannot be submitted through each other's endpoint.

Repository navigation consumes an injected RepoProjectionClient implementing the port.repo page reader. When supplied to create_app, the client is refreshed at startup; a host scheduler can call the same idempotent RepositoryNavigationService.refresh() for later full or incremental generations. The client owns transport and credential routing—host paths and credentials never enter projection payloads. Migration 0003_repository_navigation stores active state, normalized repositories, and derived facets atomically.

Production can configure the built-in HTTP adapter with HUB_CORE_REPO_MANAGER_BASE_URL; optional HUB_CORE_REPO_MANAGER_API_TOKEN bearer authentication and HUB_CORE_REPO_MANAGER_TIMEOUT_SECONDS remain transport concerns. The runtime refreshes at startup and every HUB_CORE_REPO_PROJECTION_REFRESH_SECONDS (default 300). A failed scheduled refresh preserves the last generation as stale; set the interval to 0 only when an external scheduler owns refresh.

The repository query accepts repeated primary/secondary domain, category, capability-tag, business-stake, and business-mechanic filters. Values within a family are ORed and families are ANDed. Opaque cursors are bound to the active content hash and normalized filters; a rebuild or changed filter returns 409. Every response includes source snapshot/revision, checked/rebuilt times, and a canonical content hash. MCP exposes the same surface through query_repository_navigation and get_repository_navigation_facet. No classification-write endpoint exists.

Workload projection uses a distinct injected WorkloadProjectionClient and the authoritative helixforge.workload-reference/v1 identity pair. Migration 0004_workload_projection stores only normalized transport rows and source provenance. List and /resolve routes require explicit rapp/name/deployable values and return unknown instead of consulting owners, repository names, paths, or aliases. MCP exposes query_workloads and resolve_workload_reference over those GET-only routes.

Backend boundary and readiness

The app is created with an injected PortStore. InMemoryPortStore remains available for deterministic tests and local contract smokes. Production uses PostgresPortStore, whose registration, messaging, progress, interaction, compatibility, import-lineage, and audit records are transactionally durable. Repository navigation readiness is ok, stale, unavailable, or not_applicable depending on whether a port.repo client is configured and a valid generation has been accepted.

GET /healthz proves the process is alive. GET /readyz fails with HTTP 503 when the active backend does not match HUB_CORE_BACKEND, or when the memory backend is used without explicit permission. Development and test permit it by default; the OCI image sets production-safe defaults:

HUB_CORE_ENV=production
HUB_CORE_BACKEND=memory
HUB_CORE_ALLOW_EPHEMERAL=0

Set HUB_CORE_BACKEND=postgresql and HUB_CORE_DATABASE_URL in production. Readiness checks the database and continues to fail closed if the selected backend is unavailable. A local image smoke may opt in to memory with HUB_CORE_ALLOW_EPHEMERAL=1.

The compatibility adapter is enabled by coherent route groups in HUB_CORE_V2_GROUPS; writes require the corresponding value in HUB_CORE_V2_WRITE_GROUPS. Startup rejects overlap with CORE_HUB_V2_WRITE_GROUPS. Supported groups are system, registry, credentials, interaction, and deferred. Static operator authentication uses HUB_CORE_API_TOKEN; imported consumer keys are verified by hash.

Migration bundles cover all seven Core Hub source tables. They preserve stable IDs and timestamps, reject secret-shaped material and digest drift, record counts/identity sets/canonical hashes, and return the original import-run ID when the same bundle is replayed. Reverse export contains prefixes and hashes, never raw keys.

OCI image

The Containerfile uses uv.lock with uv sync --frozen, installs the runtime extra, runs as UID/GID 10001, includes OCI version/revision labels, and exposes API port 8010 plus MCP port 8011.

docker build -f Containerfile \
  --build-arg VERSION=0.2.0 \
  --build-arg VCS_REF="$(git rev-parse HEAD)" \
  -t hub-core:dev .

docker run --rm -p 8010:8010 \
  -e HUB_CORE_ALLOW_EPHEMERAL=1 \
  hub-core:dev

Deployment charts, secrets, rollout policy, and live evidence remain owned by the rapp/platform repositories. /api/v2 data and compatibility routes remain on Core Hub until the T06/CORE-WP-0010 dual-run slices move them.

Conformance

The public-port Tier 2/3 scaffold is documented in docs/conformance.md. Run it against an isolated runtime with hub-core conformance --base-url <url>.

Core Hub absorption

docs/core-hub-absorption-plan.md defines the capability-sized /api/v2 route and data move order, single-writer dual-run controls, evidence gates, rollback, and final cutover criteria shared with CORE-WP-0010.