Commit Graph
52 Commits
Author SHA1 Message Date
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
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
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
Admin 413a0955ed feat: add license bot workflow 2026-08-04 00:09:27 +01:00
Admin e0c431960d feat(git): add license header file 2026-08-04 00:06:21 +01:00
Admin 3f9fe07c7b fix(containers): fall back to image tag when version metadata is empty 2026-08-03 14:22:52 +01:00
Admin bbc5db473a feat(frontend): redesign console UI with new component tree and design system 2026-08-03 14:22:50 +01:00
Admin 0a1c219941 feat(frontend): extract console data layer into api/domain/hooks modules 2026-08-03 14:22:45 +01:00
Admin 836153ccb5 fix: Resolve repository URL for install/update (registry + install fallbacks) 2026-08-01 23:59:39 +01:00
Admin 678356bd5b feat: Pin installed services to semver images and normalize registry versions 2026-08-01 20:58:11 +01:00
Admin d24fe0ffbf feat: Implemented (un)install,start/stop flow and fixed actions flow 2026-08-01 20:39:27 +01:00
Admin eb01569f7d feat: implement action proxy route 2026-07-31 20:30:47 +01:00
Admin a40dbe0f6e feat: implement os-based env variables instead of static code-based env variables 2026-07-31 20:30:09 +01:00
Admin 0accff3e19 real init 2026-07-29 01:36:09 +01:00
Admin bb8c94f06e Initial commit 2026-07-29 01:33:35 +01:00