2026-08-21 10:58:03 +02:00
# Hub Core runtime
2026-08-21 16:16:42 +02:00
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.
2026-08-21 10:58:03 +02:00
## Processes
The `runtime` dependency extra installs one `hub-core` command with three
process modes:
```bash
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://...
2026-08-21 16:16:42 +02:00
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
2026-08-21 10:58:03 +02:00
```
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 |
2026-08-22 00:40:42 +02:00
| `port.projection.query` | `GET /ports/projections/repository-navigation/repositories` | Snapshot-bound cross-repository classification navigation |
2026-08-23 10:54:40 +02:00
| `port.projection.query` | `GET /ports/projections/workloads` | Normalized authoritative workload projection and exact resolution |
2026-08-21 10:58:03 +02:00
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.
2026-08-22 00:40:42 +02:00
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.
2026-09-01 00:49:30 +02:00
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.
2026-08-22 00:40:42 +02:00
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.
2026-10-13 23:33:42 +02:00
### `RM_SLICE_TOPICSPINE` (A5 topic-spine rollback switch)
`RM_SLICE_TOPICSPINE` controls whether hub-core serves the A5 topic-spine
reads. These are the legacy topic aliases on the navigation responses. It
takes one of three values:
| Value | Behaviour |
| --- | --- |
| `off` (default) | Unchanged output: `legacy_topic_aliases` is `[]` , there are no extra headers, and the alias seed is not loaded. |
| `shadow` | Serves the checked-in aliases so the A5 comparison can run. Responses carry `X-RM-Slice-Topicspine: shadow` and `X-Legacy-Topic-Alias-Set: <version>` . Callers have not switched. |
| `on` | Same payload with header value `on` . hub-core is now the switched A5 read source. |
Any other value fails startup. In `shadow` and `on` , a bad alias seed also
fails startup. When the flag is not `off` , `/readyz` reports
`rm_slice_topicspine` .
**Rollback:** set `RM_SLICE_TOPICSPINE=off` and restart. State Hub's
`GET /topics/` and `GET /topics/{id}` remain the fallback throughout.
Confirm the rollback with packet case A5-C10. The flag does not re-point
callers; each caller owner switches its own base URL. Do not move the flag
from `off` in any environment until HUB-WP-0006-T06's switch window is
approved.
2026-08-23 10:54:40 +02:00
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.
2026-08-21 10:58:03 +02:00
## Backend boundary and readiness
2026-08-21 16:16:42 +02:00
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.
2026-08-22 00:40:42 +02:00
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.
2026-08-21 10:58:03 +02:00
`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:
```text
HUB_CORE_ENV=production
HUB_CORE_BACKEND=memory
HUB_CORE_ALLOW_EPHEMERAL=0
```
2026-08-21 16:16:42 +02:00
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
2026-08-21 10:58:03 +02:00
`HUB_CORE_ALLOW_EPHEMERAL=1` .
2026-08-21 16:16:42 +02:00
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.
2026-08-21 10:58:03 +02:00
## 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.
```bash
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` .
2026-09-28 11:44:50 +02:00
## Access enforcement candidate
See [access profile v1 ](access-profile-v1.md ). Production now defaults to the shared
access boundary; the default factory fails closed until real identity/facts/policy/
audit adapters are composed. Only exact GET `/healthz` is public. Do not deploy this
source candidate over the current release before the HUB-WP-0012 admission gates.
Development/test retains the existing unauthenticated/native and legacy-key lanes.