Commit Graph
62 Commits
Author SHA1 Message Date
Admin b7fa29849d [fix](maestro): pair services by declared identity and show web/source chips
Image-name comparison split on the first colon, so every image on a registry with a port looked like a different service (or the same one); split on the last colon before the tag and strip the registry host when present. Pair a catalog entry with a container by org.dcntrlized.service first, then by repository name, and never with a registry-only placeholder. The card gains the resolved Web app chip and renders a source link only when the server validated it, with the raw value inert otherwise.
2026-10-19 02:40:26 +01:00
Admin 4038334c28 [feat](maestro): resolve web app links from owner-controlled sources
A service announces a web block only while its listener is enabled and serving, and the manifest own href is ignored. Resolve the URL server-side with the precedence manifest url > per-service operator override > global base-domain template > request host plus published port, validate every source at resolution time, and omit a block that has no usable target. The list endpoint resolves a fresh copy, so cached containers are never mutated and the console only ever opens a server-produced href.
2026-10-19 02:40:26 +01:00
Admin 786b7a3b5f [feat](maestro): publish an operator-authored app catalog
An instance distributes installable Android apps independently of the services it runs. Add <dataRoot>/catalog/apps.json (owner-authored, atomic, 0600) with hosted-or-linked artifacts, SHA-256 and optional icon; expose the owner-gated add/edit/delete/replace API plus session-free artifact and icon downloads, and add the apps section to the public profile. The catalog is deliberately independent of services and the registry, and registry listings gain the declared service identity and a validated source href so cards can be paired correctly.
2026-10-19 02:40:05 +01:00
Admin 13f6d14521 [feat](maestro): adopt externally managed services without owning them
The dashboard sees containers Maestro did not create (each repo dev debug compose), and treating them as installs made start/uninstall/config act on a project file that never created them. Mark them external, refuse start/update/uninstall with a message pointing at their own compose project, and let stop work at the container level. Inject runtime env (config-signing public key, plaintext shared-DB DSN) on the managed start path, and keep the external flag a property of the service so a debug sibling cannot flip a managed service.
2026-10-19 02:40:05 +01:00
Admin 7afd019215 [feat](maestro): proxy one data plane instead of per-service routes
Apps and phones reach services through the instance, so only one port per instance needs to be open and the service answers its own 401. Add handlers/dataplane: one allowlist (/api/{service}/action/{action}, /auth/{path...}, /ws[/...]) and one dispatcher that chooses HTTP or WebSocket by upgrade presence, outside the owner gate because callers hold no Maestro session. Both it and the legacy action proxy resolve through lib/serviceproxy, so the two edges cannot disagree about a service address.
2026-10-19 02:40:05 +01:00
Admin 17d5307110 [refactor](maestro): retire the PKI and federation identity layer
Contract rev 9 removes the instance CA, client certificates, the peer listener and the trust store fleet-wide. Internal traffic is plaintext HTTP on each service single :8080 listener, so there is no peer identity left to authorize: delete lib/pki, lib/federation, devboot, the per-service database role model and the /api/peers* and /api/instances surfaces, and drop the console Trusted services UI. Config delivery now rides a dedicated ES256 token from lib/configsign rather than mTLS, and the instance fingerprint is derived from a persisted random key and is informational only. main.go also wires the surfaces introduced by the later commits, which this change is what makes possible.
2026-10-19 02:39:15 +01:00
Admin 3220944b12 ci: make the advisory license check work on the egress-free runner
The hardened runner's job network has no internet access, so `apk add` and
downloading addlicense from GitHub cannot work. git, curl, jq and addlicense
are baked into the runner's job image (infra/gitea-runner/ci-tools.Dockerfile).
2026-10-07 23:46:08 +01:00
Admin 25dd72f1f0 ci: reword the advisory license workflow (the bot adds a separate commit, it never amends) 2026-10-07 03:29:02 +01:00
Admin c84d5d2210 ci: replace the license workflow with a read-only advisory check
The license bot service (Dcntrlized/license-bot) is authoritative: it amends
missing AGPL headers into the PR head commit, including for fork PRs, and
reports the license-bot / addlicense commit status. This workflow now only
reports an honest advisory signal, holds no secrets and never writes.
2026-10-07 02:25:16 +01:00
Admin ea34af29f9 ci: fix AGPL license bot (runner label alpine-latest, HTTP scheme, amend + comment) 2026-10-07 00:44:01 +01:00
Dcntrlized Auto CopyLeft Bot c607c0baa9 chore: add AGPL-3.0 license headers 2026-09-03 20:54:36 +00:00
Admin 1558867d46 feat(config): deliver to always-mTLS Maestro peer edge (CN=maestro gate)
- /config delivery now targets the service's dedicated Maestro peer listener:
  https + authmgmt.MaestroTransport() (Maestro's own mTLS identity, never
  downgraded in dev), port from org.dcntrlized.peer_port label (default 9443)
- extract peerPort() label resolver + unit test
- expose MaestroTransport() on authmgmt for orchestrator-only edges
- spec + comments updated to describe the gated peer edge
2026-09-02 09:37:02 +01:00
Admin c540ad5d7f feat(config): fail-closed manifest-driven injection + runtime write-back delivery (D6) 2026-08-31 09:43:43 +01:00
Admin a9e2182396 feat(config): per-service operator config + secrets (theme M1)
Services can declare an operator-configurable config block in /_manifest
(ordered array of single-key objects, pure schema: type cleartext|password,
description?, key = container env var name). Maestro:

- lib/secrets: AES-256-GCM FileStore; dedicated secrets.key (32 bytes,
  chmod 600, separate from session.key); secrets.json rows keyed
  service/field; cleartext read-write, password write-only & encrypted at
  rest, decrypted only at injection. Footure key rotation = delete the key.
- handlers/config (owner-gated via auth.RequireSession):
  GET /api/services/{service}/config (merged schema+set view, password
  values never returned), PUT/DELETE /api/services/{service}/config/{key}
  (uniform ERR_NOT_FOUND / ERR_INVALID_PAYLOAD error shape).
- start.go: injectConfigSecrets runs after injectServiceIdentity; only
  operator-set rows become env on non-oneoff services; unset fields keep
  the compose default.
- Manifest.Config carried onto Container/RegistryContainer/InstalledMetadata
  via updateInstalledMetadataFromManifest + watcher.
- Frontend: per-service Settings dialog (ServiceConfigDialog) + api
  helpers; cleartext editable, password set/unset indicator only.
- spec.yaml documents the new endpoints/schemas.
2026-08-31 00:48:51 +01:00
Admin ca4c66110e chore(vscode): unify Launch/Follow/Stop Backend tasks 2026-08-29 15:31:55 +01:00
Admin 1973b93ca8 chore(vscode): add Start/Stop Maestro compose tasks 2026-08-29 15:08:53 +01:00
Admin c014b2abfb fix(auth): hand off privileges before state creation; fail closed on store errors
Fresh boots opened the owner/token/session stores before EnsureDataRoot
dropped to the host user, leaving a root-owned maestro/ state dir whose
EACCES surfaced as a bogus ERR_INVALID_CREDENTIALS on /api/auth/setup.
main() now runs the handoff first; EnsureDataRoot heals pre-existing
root-owned state dirs while still root (healOwnerState).

Storage failures from the owner store now answer 500 ERR_INTERNAL
everywhere (status/setup/login/change-password/RequireSession/
GateWebSocket) instead of blaming credentials or re-advertising setup
mode; SetupRequired returns the store error instead of swallowing it,
and FileOwnerStore.Verify propagates read errors rather than masking
them as unknown-user.
2026-08-23 12:10:15 +01:00
Admin 707fe619f7 fix(instance): sort public services so the profile order is stable
The cache is a Go map; unsorted output reshuffled on every poll,
which made hub-client lists visibly jump around.
2026-08-23 12:03:55 +01:00
Admin 5d11e80837 feat(instance): announce-domain naming default + capitalized fallback
Identity chain is now instance.json → DCNTRLIZED_INSTANCE_NAME →
DCNTRLIZED_INSTANCE_DOMAIN (the domain the instance announces as,
e.g. maestro.dcntrlized.org) → human-readable hostname → 'Maestro'.
2026-08-23 12:03:55 +01:00
Admin 76224b028d fix(instance): never serve a container-id hostname as the instance name
os.Hostname() inside the Maestro container is the bare container id
(e.g. 6079761d6e4d) — a hex-only hostname now falls back to 'maestro'
so the public profile stays meaningful out of the box. DCNTRLIZED_INSTANCE_NAME
and instance.json still win.
2026-08-23 11:48:23 +01:00
Admin 98341c7665 feat(instance): public profile endpoint GET /api/instance (hub-app M1) 2026-08-23 01:02:29 +01:00
Admin 85b4b4cd91 feat(transport): loopback-only default port binding + reverse-proxy docs (onboarding M3) 2026-08-23 00:39:53 +01:00
Admin 2ffdf77905 feat(auth): instance-owner gate — owner store, JWT sessions, setup/login UI (onboarding M1+M2)
lib/owner: argon2id credential store, HS256 access/refresh pair with kid,
hashed-jti session store, throttler (5 free failures then 4^n backoff).
handlers/auth: /api/auth/* + RequireSession/CSRF/GateWebSocket middleware;
every /api/* route is now gated (428 in setup mode). --reset-owner recovery.
Frontend: AuthScreen (setup/login), useAuth gate hook, gated data hooks +
single-flight silent refresh, Owner account section in settings.
spec.yaml documents the full surface.
2026-08-22 22:58:18 +01:00
Admin cf51a6be94 fix(db): run schema-level grants against the shared database, not postgres 2026-08-21 21:05:23 +01:00
Admin c916dd4ac8 feat(db): per-service role DSNs on an armed shared DB (M2 §4)
Database-capable services now get a per-service DATABASE_URL injected at
start (role = cert CN, sslmode=verify-full against the instance CA) when
the shared database runs mTLS-armed; one-off migrate/seed helpers get the
identity mount plus libpq SSL env under the same role. SyncAuthorization
grants each peer CREATE on public and USAGE + ALL on tables/sequences in
every user schema so per-service roles can run migrations and CRUD.
2026-08-21 20:59:42 +01:00
Admin 545338016e feat(peers): settings dialog + hide for revoked peers, pending badge in header 2026-08-21 20:59:42 +01:00
Admin 3217dca39f feat(db): dbctl — reconcile per-peer CONNECT authorization on the shared DB (M2 §4)
New lib/db/dbctl: SyncAuthorization runs after the DB starts (EnsureDatabaseRunning),
ensuring a LOGIN role + CONNECT grant per installed peer and revoking the stock
PUBLIC CONNECT grant on mTLS-armed databases; RevokeForService drops a peer's
CONNECT at uninstall. Admin executes over the in-container unix socket as
postgres (trust). Wired into ensureInfraRunning and UninstallContainer.
Includes unit tests + env-gated live integration test.
2026-08-19 00:22:23 +01:00
Admin 73fd96d698 feat(peers): full trust-store UI — accept, reject, revoke, re-authorize
- /api/peers/reject forwards to the auth service's new /internal/reject
  (pending request denied + audited; peer stays on the no-op path).
- TrustedServices panel now has three sections: Requested (Accept/Reject),
  Accepted (Revoke), and Revoked (Re-authorize), with timestamps.
- The old pending 'Ignore' button mapped to revoke, which was the wrong
  operation for a never-approved request.
2026-08-17 00:27:42 +01:00
Admin becf3043e3 fix(registry): dev never falls back to the public registry
BaseURL() in dev mode used to silently fall back to https://git.dcntrlized.org
when DCNTRLIZED_GITEA_URL was unset — a dev Maestro could resolve installs and
infra from the wrong (and cred-less) registry. Dev now uses the debug registry
exclusively; an unset URL returns '' so registry ops fail loudly. Locked with
a BaseURL seam test.
2026-08-16 17:18:16 +01:00
Admin e66cf54d5b feat(devboot): remove dev fleet pre-approval
Dev now runs the real trust-store flow end to end: satellites boot with the
instance-CA identities Maestro issues (devboot.EnsureIdentities), but their
first mTLS contact records a pending authorization and is refused (403) until
the owner approves via /api/peers. The ApproveFleet shortcut is gone, along
with the authmgmt dependency in devboot.
2026-08-16 16:52:49 +01:00
Admin cda3091ad8 fix(containers): remap identity bind sources to host paths in dev
The dev maestro container's data root is /data/.dcntrlized
(XDG_DATA_HOME=/data), but bind-mount sources are resolved on the daemon's
host namespace — a nonexistent /data/.dcntrlized/... source made Docker
create empty directories, so the auth hub saw /dcntrlized/pki/tls.crt as a
directory and TLS setup failed. DCNTRLIZED_HOST_DATA_ROOT (compose-injected,
/home/franciscofadigas/.dcntrlized) is now substituted for the container-internal prefix when
building identity bind sources. hostBindSource leaves paths unchanged on host
installs.
2026-08-16 16:34:46 +01:00
Admin e13831ca20 fix(infra): infraRunning must match the canonical container name
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.
2026-08-16 16:05:06 +01:00
Admin addaabc0a8 feat(dev): DCNTRLIZED_DEV drives dev mode; dev fleet runs real mTLS
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.
2026-08-16 15:51:49 +01:00
Admin e569466dc3 feat(dev): propagate INSECURE_INTERNAL=1 to compose services Maestro starts in dev mode 2026-08-16 15:08:18 +01:00
Admin 420768318c fix(dev): serve web UI in debug mode — real frontend embed + data mounts in debug compose 2026-08-16 03:27:17 +01:00
Admin 483ed3b531 feat(infra): auto-install and start shared database/auth at boot when missing
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.
2026-08-16 03:05:30 +01:00
Admin 92f4787146 M2 §2: run actions and manifest fetch over mTLS with Maestro's identity
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).
2026-08-16 01:12:01 +01:00
Admin 77bd21aea2 M2 §5: pin auth attestation into InstalledMetadata and inject AUTH_PINNED_FINGERPRINT
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).
2026-08-15 23:43:02 +01:00
Admin 950b6e5827 feat(security): trusted-services surface + auth mgmt client (M2 §3)
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.
2026-08-15 23:27:21 +01:00
Admin dc79629c7e feat(security): instance CA + service identity certs (M1)
- 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)
2026-08-15 23:10:12 +01:00
Admin a172b542b7 dev: point dev infra at 192.168.122.183 2026-08-15 18:08:27 +01:00
Admin 642852f3fc feat(registry): add DCNTRLIZED_DEV/GITEA_URL seam for dev registry 2026-08-15 16:27:57 +01:00
Admin d6482f5592 feat(auth): surface manifest has_auth/public_key and ensure shared auth running
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.
2026-08-15 00:23:09 +01:00
Admin a008245db9 feat(actions): resolve containers by label and forward full requests
- 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.
2026-08-10 16:28:06 +01:00
Admin b76c02f807 feat(frontend): show database status chip on capability cards
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.
2026-08-10 16:28:02 +01:00
Admin c1ad85b65c feat: provision shared database as hidden infrastructure
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.
2026-08-10 16:27:58 +01:00
Admin d2fb10ca44 fix: Tighten license autocommit bot to PRs, implement .licenseignore for ignoring specific files or directories 2026-08-04 23:34:46 +01:00
Dcntrlized Auto CopyLeft Bot 04935427a1 chore: add AGPL-3.0 license headers 2026-08-04 22:02:50 +00:00
Admin 975519f451 change default shell for workspace 2026-08-04 23:02:41 +01:00
Admin 6645524432 specify shell as sh 2026-08-04 11:58:56 +01:00