auth-debug carries the same org.dcntrlized.service=dcntrlized-auth role label,
so the shared-auth check considered it running and skipped starting the real
hub. Match the exact container name (dcntrlized-auth) on the running
candidate; dev lookalikes sharing the role label no longer satisfy the check.
The single canonical dev flag is DCNTRLIZED_DEV=true, injected by every
docker-compose.debug/dev.yaml. Dev mode now selects endpoints and behaviors
without downgrading control-plane security:
- lib/env: IsDev()/GiteaURL(); registry BaseURL keys on DCNTRLIZED_DEV and
DCNTRLIZED_GITEA_URL (renamed from GITEA_URL).
- authmgmt: Scheme()/PeerTransport() (satellite edges) key on DCNTRLIZED_DEV
— dev satellites keep a plaintext RN-facing listener, production mTLS. The
auth-management edge (New()) stays mTLS in both, preferring
DCNTRLIZED_AUTH_URL in dev.
- Removed INSECURE_INTERNAL + propagateInsecureInternal from Maestro: the
orchestrator no longer runs plaintext, and no longer injects the escape
hatch into services it starts.
- lib/devboot: at boot (dev only) Maestro provisions the identities its
compose-managed peers bind-mount at /dcntrlized/pki (before infra boots)
and approves the known fleet in the auth trust store. Unknown peers still
record a pending authorization and are refused (mechanisms active).
- Composes: prod docker-compose.yaml is clean (no DCNTRLIZED_DEV/GITEA_URL);
the debug compose carries the dev flag + DCNTRLIZED_GITEA_URL only.
ensureInfraRunning now resolves the missing infra service from the registry
(via the new lib/registry package, extracted from the GetRegistry handler so
the UI and the bootstrap share one source of truth) and installs it before
starting it. A fresh host boots with its database and auth service without
manual UI steps; failures stay non-fatal warnings like before.
Extract maestroTransport() (Maestro's identity cert + instance CA) and
expose PeerTransport()/Scheme()/InsecureInternal() so every service peer
edge uses the same client. RunAction now targets
https://{ip}:{port}/action/{name} presenting Maestro's cert (services
require a CA-issued client cert, so only Maestro can drive actions — T4)
and FetchManifestWithRetry fetches /_manifest the same way.
INSECURE_INTERNAL=1 keeps the dev fleet plaintext (debug compose updated
to set it).
Fetch the auth service's key attestation over mTLS at install time and
persist the fingerprint (plus kid/issued-at) in the install record. At
start, inject AUTH_PINNED_FINGERPRINT into every non-oneoff service so
the shared authclient refuses a substituted key instead of silently
trusting it (TOFU/D3). Best-effort: installs that cannot reach the auth
service backfill the pin on the next start. Skip pinning for the auth
service itself (it is the attester).
Maestro is the UI/CA, not the enforcement: the auth service owns its
authorized-keys trust store. This is Maestro's side of the flow.
- lib/authmgmt: mTLS management client (Maestro's own identity cert from
the instance CA) for /internal/{attestation,authorizations,revoke,
pending}; RevokeServiceBestEffort for uninstall.
- handlers/peers: GET /api/peers, /api/peers/attestation, POST
approve/revoke.
- uninstall: revoke the service's authorization in the auth trust store
alongside the CRL identity revocation.
- frontend: TrustedServices panel (pending first-contact authorizations +
approved keys with authorize/ignore/revoke actions).
- spec.yaml: document the new endpoints and schemas.
- housekeeping: untrack pki test log artifacts, gofmt fixes in pki.
- lib/pki: root CA provisioning on first run (~/.dcntrlized/pki/), per-service
leaf identity issuance (90d, refreshed at start), revocation with CRL
- main: EnsureCA on startup
- start: inject CA + identity mounts and AUTH_* mTLS env into compose services
- uninstall: revoke the service's cert (CRL entry published)
Parse has_auth/public_key from the service manifest onto Container
(persisted to InstalledMetadata), fetch manifests for running non-db
containers during reconciliation, hide role=auth containers from the
grid, and ensure dcntrlized-auth starts with the shared database.
- New ResolveContainer inspects a container by id, name, service label
or image name (case-insensitive), falling back to label matching when
Docker cannot resolve the reference.
- The action proxy uses the org.dcntrlized.port label, falling back to
the first mapped guest port, and no longer hardcodes the host port.
- Action forwarding preserves the request method, body, query string and
hop-by-hop-safe headers, and passes non-2xx status and body through.
Service cards that declare the database capability render a status chip
for the shared dcntrlized-db infra service, with a Pull action to
install and start the database when it is not installed yet.
- Merge logic hides db-role containers and hidden registry items from
the grid.
- Service model carries capabilities; a new DatabaseChip maps the live
database state onto the existing status metadata.
The shared dcntrlized-db Postgres service is now treated as Maestro
infrastructure: it is hidden from the service grid and auto-started
before any app container when already installed.
- Container model gains role/hidden/capabilities; registry items gain
hidden; installed metadata persists manifest capabilities so stopped
services still surface them.
- Registry marks the shared database image as hidden (OCI annotation,
label, or name fallback).
- Compose label rewrite merges existing service labels instead of
replacing them, so org.dcntrlized.role/hidden survive.
- New infra helpers ensure the external dcntrlized_net network and
start the database on the prod path and before each StartContainer.
- Watcher skips manifest fetch for db-role containers and persists
capabilities from the manifest.
- spec.yaml documents the new fields.