# Repository classification publisher Repo Manager publishes the frozen `helixforge.repository-classification-projection` 1.0.0 envelope at: ```text GET /ports/repositories/classifications?cursor= ``` The route is read-only. It contains registrar UUID, slug, lifecycle, classification, Git revision, source fingerprint, and observation time. Local paths, remotes, work records, source bodies, and credentials never enter the envelope. ## Forgejo-backed registry Publication joins repository-owned `.repo-classification.yaml` files to a stable repository registry. Production uses the committed `config/repository-registry.yaml`; it contains registrar UUIDs and canonical Forgejo identities, but no credentials or host paths: ```yaml schema: repo-manager.repository-registry.v1 repositories: - repository_id: a9d105c6-10fa-4cf9-8bc1-e248476698d3 slug: repo-manager lifecycle: active forgejo_repository: coulomb/repo-manager ``` Bootstrap current stable identities from the retiring primary State Hub: ```bash rmgr publisher import-registry \ --root /home/operator \ --api-base http://127.0.0.1:8000 \ --source forgejo \ --output ~/.repo-manager/repository-registry.yaml ``` The import verifies `instance_role=primary`, discovers only classified Git working copies below the selected root, and uses bounded direct identity reads. It does not use State Hub's expensive bulk repository projection and does not trust State Hub host paths. Transient reads retry; locally classified but unregistered checkouts are retained as warnings and omitted rather than being assigned invented identities. Any other failure prevents replacement of the registry file. The import is a migration/bootstrap path. Normal governed registration must maintain the committed UUID-to-Forgejo mapping after State Hub registrar retirement. The publisher contract does not change. ## Run Set runtime configuration through the environment: ```text REPO_MANAGER_REGISTRY_PATH=~/.repo-manager/repository-registry.yaml REPO_MANAGER_CURSOR_SECRET= REPO_MANAGER_API_TOKEN= REPO_MANAGER_FORGEJO_BASE_URL=https://forgejo.coulomb.social REPO_MANAGER_FORGEJO_TOKEN_FILE=/var/run/secrets/repo-manager/forgejo/token REPO_MANAGER_FORGEJO_WORKERS=8 REPO_MANAGER_FORGEJO_TIMEOUT_SECONDS=10 REPO_MANAGER_FORGEJO_RETRIES=2 REPO_MANAGER_PROJECTION_PAGE_SIZE=100 REPO_MANAGER_PROJECTION_MAX_SNAPSHOTS=4 ``` The Forgejo token is optional for public repositories and required for private repositories. It must have only `read:repository`; mount it from the platform credential path rather than putting it in an environment value, Git, or logs. The cursor secret may be omitted for a single-replica runtime, in which case a fresh process-local key is generated and in-flight cursors expire on restart. Start the API with: ```bash rmgr publisher api --host 0.0.0.0 --port 8020 ``` `GET /healthz` is process liveness. `GET /readyz` verifies that configuration and the registry are usable. When `REPO_MANAGER_API_TOKEN` is set, the projection route requires the matching bearer token. ## Snapshot and failure semantics A first-page request observes the complete registry into an immutable in-memory snapshot. Repositories are ordered by registrar UUID. Subsequent page cursors carry snapshot ID, offset, and page size protected by HMAC-SHA256. Only a small bounded set of in-flight snapshots is retained; an invalid, tampered, or expired cursor returns `409`. For each repository the publisher reads metadata, resolves the default branch to an exact commit SHA, and then reads `.repo-classification.yaml` at that SHA. Missing/private repositories, invalid classifications, or unavailable revisions are emitted as bounded error diagnostics. Hub-core rejects any such transfer and keeps the last accepted generation, so a partial observation never becomes the active navigation projection. ## Hub-core client Configure the hub-core runtime with: ```text HUB_CORE_REPO_MANAGER_BASE_URL=http://repo-manager:8020 HUB_CORE_REPO_MANAGER_API_TOKEN= HUB_CORE_REPO_MANAGER_TIMEOUT_SECONDS=10 HUB_CORE_REPO_PROJECTION_REFRESH_SECONDS=300 ``` Hub-core refreshes once at startup and then on the configured interval. A failed refresh marks the dependency stale while preserving the last accepted generation. Setting the refresh interval to `0` disables the background loop. ## Verification ```bash make test make publisher-snapshot \ REGISTRY=~/.repo-manager/repository-registry.yaml \ CURSOR_SECRET='' ``` The workstation conformance accepted all 123 local classifications. Forgejo conformance resolves 114 public repositories without credentials and correctly rejects the nine private `rapp-*` sources until a least-privilege source-read token is mounted. No State Hub classification table is read at runtime.