From cda3091ad81a6dc2e40681cf70fcdfc6f3e6f533 Mon Sep 17 00:00:00 2001 From: Admin <1+admin@noreply.git.dcntrlized.org> Date: Sun, 16 Aug 2026 16:34:46 +0100 Subject: [PATCH] fix(containers): remap identity bind sources to host paths in dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- docker-compose.debug.yaml | 4 ++++ src/lib/containers/start.go | 23 ++++++++++++++++++++--- src/lib/containers/start_test.go | 23 +++++++++++++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/docker-compose.debug.yaml b/docker-compose.debug.yaml index ed8bd56..b947040 100644 --- a/docker-compose.debug.yaml +++ b/docker-compose.debug.yaml @@ -16,6 +16,10 @@ services: environment: - DCNTRLIZED_DEV=true - XDG_DATA_HOME=/data + # Host path backing the data root, so Maestro's containerized start path + # can produce bind-mount sources the daemon resolves on the host + # namespace (identity injection for infra/peers). + - DCNTRLIZED_HOST_DATA_ROOT=${XDG_DATA_HOME:-${HOME}}/.dcntrlized - DCNTRLIZED_GITEA_URL=${DCNTRLIZED_GITEA_URL:-http://192.168.122.183:3000} - GITEA_REGISTRY_RO_TOKEN=${GITEA_REGISTRY_RO_TOKEN} - GITEA_RW_PACKAGE_TOKEN=${GITEA_RW_PACKAGE_TOKEN} diff --git a/src/lib/containers/start.go b/src/lib/containers/start.go index ff050a4..1297a47 100644 --- a/src/lib/containers/start.go +++ b/src/lib/containers/start.go @@ -18,6 +18,7 @@ package containers import ( "context" "fmt" + "os" "path/filepath" "strings" @@ -130,9 +131,9 @@ func injectServiceIdentity(ctx context.Context, project *composetypes.Project, s } mounts := []string{ - fmt.Sprintf("%s:%s/ca.crt:ro", pki.CertPath(dataRoot), identityMountDir), - fmt.Sprintf("%s:%s/tls.crt:ro", identity.CertPath, identityMountDir), - fmt.Sprintf("%s:%s/tls.key:ro", identity.KeyPath, identityMountDir), + fmt.Sprintf("%s:%s/ca.crt:ro", hostBindSource(dataRoot, pki.CertPath(dataRoot)), identityMountDir), + fmt.Sprintf("%s:%s/tls.crt:ro", hostBindSource(dataRoot, identity.CertPath), identityMountDir), + fmt.Sprintf("%s:%s/tls.key:ro", hostBindSource(dataRoot, identity.KeyPath), identityMountDir), } env := map[string]string{ "SERVICE_NAME": serviceName, @@ -161,6 +162,22 @@ func injectServiceIdentity(ctx context.Context, project *composetypes.Project, s return nil } +// hostBindSource maps a dataRoot-relative path to a bind-mount source the +// Docker daemon can resolve. Bind sources are resolved on the daemon's host +// namespace, but inside the dev container the data root lives at a +// container-internal path (/data/.dcntrlized via XDG_DATA_HOME=/data) that +// does not exist on the host — the daemon would silently create it as an +// empty directory. DCNTRLIZED_HOST_DATA_ROOT (compose-injected, e.g. +// $HOME/.dcntrlized) carries the host path backing the data root; when set, +// the container-internal prefix is swapped for it. On a host install the path +// is already host-resolvable and returned unchanged. +func hostBindSource(dataRoot, p string) string { + if host := os.Getenv("DCNTRLIZED_HOST_DATA_ROOT"); host != "" && strings.HasPrefix(p, dataRoot) { + return filepath.Join(host, strings.TrimPrefix(p, dataRoot)) + } + return p +} + // pinAuthFingerprint returns the service's pinned auth fingerprint, fetching // and persisting it when the install record does not have one yet (M2 §5). // Best-effort: when the auth service is unreachable, it returns "" and the diff --git a/src/lib/containers/start_test.go b/src/lib/containers/start_test.go index 87830f1..61c0723 100644 --- a/src/lib/containers/start_test.go +++ b/src/lib/containers/start_test.go @@ -14,3 +14,26 @@ // along with this program. If not, see . package containers + +import ( + "testing" +) + +func TestHostBindSource(t *testing.T) { + t.Setenv("DCNTRLIZED_HOST_DATA_ROOT", "") + got := hostBindSource("/data/.dcntrlized", "/data/.dcntrlized/pki/services/auth/tls.crt") + if got != "/data/.dcntrlized/pki/services/auth/tls.crt" { + t.Fatalf("without host root the path must pass through, got %q", got) + } + + t.Setenv("DCNTRLIZED_HOST_DATA_ROOT", "/home/user/.dcntrlized") + got = hostBindSource("/data/.dcntrlized", "/data/.dcntrlized/pki/services/auth/tls.crt") + if want := "/home/user/.dcntrlized/pki/services/auth/tls.crt"; got != want { + t.Fatalf("host root must replace the container-internal prefix, got %q want %q", got, want) + } + + got = hostBindSource("/data/.dcntrlized", "/data/.dcntrlized/pki/ca.crt") + if want := "/home/user/.dcntrlized/pki/ca.crt"; got != want { + t.Fatalf("ca path must be remapped too, got %q want %q", got, want) + } +}