32 Commits
Author SHA1 Message Date
dsh-relay 0af110766d ci: pick the webserver bind address from the installed schema
Sync upstream and build images / sync-and-build (push) Successful in 38m42s
The 0.2.1-alpha.2 fix rendered `host` onto the container's own address
unconditionally. Every earlier release still validates it against the union of
the two literals 127.0.0.1 and 0.0.0.0, so those images now die in the boot
smoke test with

  @deepseek-ai/dsh-host-webserver: ValidationError: invalid config:
    - $.host expected "127.0.0.1" | "0.0.0.0" but got "172.17.0.22" (at host)

and their refresh rebuilds -- which the recipe-hash change re-queued anyway --
can no longer succeed. Probe the installed webserver schema instead: where it
rejects the unspecified address render the container's own IPv4, otherwise keep
0.0.0.0 so older versions build exactly as they did before. The probe is
overridable through DSH_INSTALL_ROOT so both branches are testable.
2026-10-13 21:09:56 -04:00
dsh-relay 8363be933d Bind the web UI to the container's own address: dsh 0.2.1+ rejects 0.0.0.0
Sync upstream and build images / sync-and-build (push) Successful in 24m26s
dsh 0.2.1-alpha.2 validates `webserver.host` in a schemastery transform and
throws on the unspecified address ("is an unspecified (wildcard) address,
which is not supported: binding every interface would expose remote code
execution to the network; bind one concrete IPv4 or IPv6 address of a local
interface instead"). The check is config-level, so the --patch opt-in that
carried 0.0.0.0 through the 0.1.x schema no longer works, and every build of
0.2.1-alpha.2 has failed the boot smoke test before the image could be pushed
(the tag exists, so the release page has no image and no notes).

The shipped patch becomes a template; docker-entrypoint.sh renders it onto the
container's first non-internal IPv4 (the same address the smoke test probes)
and passes the rendered copy to dsh, so a port published on the Docker host
still reaches the UI while the socket binds one concrete interface. Set
DSH_WEB_HOST to override the address. The substitution also accepts the old
literal `host: 0.0.0.0` template, so either file can land first.
2026-10-13 20:45:00 -04:00
mward4 128ca9d8b3 ci: only a never-published version can fail the sync run
Sync upstream and build images / sync-and-build (push) Successful in 16m13s
Runs have been red since 2026-09-19 even though every release shipped: the
plan step re-plans the oldest 11 versions as "stale" on every run (pnpm and
the node base image moved on), and those versions can no longer be rebuilt at
all — their floating dependency ranges now resolve to @deepseek-ai packages
that have moved past them, so they fail the boot smoke test ~67s in. Step 8
then failed the run for them, and every 6-hour run went red for ~13 minutes of
builds that could never succeed. A green run meant nothing, and a red one no
longer pointed at anything real.

Split the plan into two kinds of build and treat them differently:

  NEW     - the registry has no image for this version. This is an upstream
            release that has not shipped here yet, which is the whole job.
  REFRESH - the registry already has the image; it is only being moved onto a
            newer pnpm/base/recipe. Best-effort.

* Plan NEW first so a new release lands in the first minutes of the run
  instead of queueing behind refreshes that may all be unbuildable.
* A failed NEW version is recorded in a `missing` output and later reported
  by the final gate; a failed REFRESH stays a warning and keeps its image.
* The final gate now fails only on `missing` or on a current version with no
  image in the registry — a missed upstream release is still visible and red.

Also removes two early aborts that silently skipped the prune and the release
publish whenever the current version failed to build: step 4 no longer exits
after a failed build, and the :latest step records its miss instead of
exiting. Publishing now always happens, so the releases for the versions that
did ship are in place even when the run legitimately ends red.
2026-09-28 14:33:27 -04:00
dsh agent c398febf0f Default the workspace doctor to repair
Sync upstream and build images / sync-and-build (push) Successful in 8m20s
Nothing repaired on a plain container update: the image defaulted to
`warn`, which reports lost grouping and stops there, so a project
directory an update erased stayed erased unless the operator set
DSH_WORKSPACE_DOCTOR=repair — a field an existing Unraid container never
receives, because Unraid does not add variables a template gained later.

Bake DSH_WORKSPACE_DOCTOR=repair into the image and make that the
entrypoint's fallback, so an update heals itself with no configuration:
missing project directories are recreated and the chats orphaned with
them are re-accounted at startup, with the registry backed up first and
session logs untouched. `warn` reports without repairing, `off` is
silent, and an unrecognised value repairs rather than silently doing
nothing.

README: the doctor is described as the default with the variable as an
opt-out, and the Unraid note now says an existing container needs no new
field.
2026-09-17 22:26:21 -04:00
dsh agent 3849709cc2 Fix the doctor's invocation path; assert the boot log in CI
Sync upstream and build images / sync-and-build (push) Successful in 8m26s
The entrypoint called /usr/local/bin/dsh-workspace-doctor while the
Dockerfile installs /usr/local/bin/dsh-workspace-doctor.mjs, so every
start logged "...: not found" and the workspace doctor never ran: no
report, no repair. The CI smoke test checked the .mjs path with a direct
`docker exec`, which cannot see a mistake in the entrypoint's own
invocation, so the build stayed green while the container was broken.

Resolve the doctor by name (`command -v dsh-workspace-doctor.mjs`) in
both the root and the non-root path, and log an explicit "not installed
in this image" line when it cannot be found, instead of the shell's bare
"not found". The smoke test now reads the container's boot log: it
requires the doctor's own "dsh-workspace-doctor:" output to appear, and
fails if the entrypoint logs its "workspace doctor:" failure line — that
covers path, exec bit and invocation together, which is the class of
mistake that shipped.

README: manual repair through a one-off container with --entrypoint (dsh
stopped, so nothing races the registry write), and the note that an
existing Unraid container does not pick up the template's new Workspace
Doctor variable on its own.
2026-09-17 22:21:58 -04:00
dsh agent c41d7fb403 Repair workspace grouping that a container update erases
Sync upstream and build images / sync-and-build (push) Successful in 9m9s
Updating the container recreates it, so anything outside the mounted
/workspace and state volume is gone. dsh joins a session to its project
when the session is created (upstream's history bootstrap runs once per
state volume and never again), and the workspace registry stores an
absolute canonical directory path — so a project directory under
/home/node, where the workspace picker opens by default, loses its chats
to "Ungrouped" permanently on the next update.

Ship dsh-workspace-doctor.mjs and run it from the entrypoint before dsh
starts: it reports every project directory that no longer resolves and
every chat recoverable from one, and with DSH_WORKSPACE_DOCTOR=repair it
recreates the missing directories and re-accounts those sessions in the
workspace registry. Session logs are never read for writing and never
modified, no project is invented, no chat is moved into a project it did
not run in, and the registry is copied to workspace.json.bak-<stamp>
before the atomic, 0600, fsynced write dsh's own JSON backend performs.
The doctor refuses to write a registry version it does not know, is
idempotent, and defaults to report-only in the image (warn) so nothing
changes unasked; the Unraid template defaults to repair.

CI: the doctor joins the recipe hash and the push path list, and the
image smoke test asserts it is installed and runs against a fresh volume
(exit 2, the tool itself failing, is the only outcome that fails a build).
2026-09-17 22:03:40 -04:00
WardandClaude Fable 5.1 14d4f2b255 Recompress exported layers before the chunked push
Sync upstream and build images / sync-and-build (push) Successful in 40s
`docker save` on a classic (non-containerd) daemon writes layers as raw tar,
and regctl pushes what it is given: 0.1.6-alpha.2 landed with a 517 MB
uncompressed layer, about 850 MB per pull instead of 190. Recompress the
layout with gzip before copying it to the registry.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-17 21:38:19 -04:00
WardandClaude Fable 5.1 a10b8efa38 Push images in chunks; fail the run when a version does not ship
Sync upstream and build images / sync-and-build (push) Successful in 1m46s
0.1.6-alpha.2 built and passed its smoke test on 2026-09-17, then the push
died: the registry sits behind Cloudflare, which rejects request bodies over
100 MB, and `docker push` sends a layer as one request. dsh's node_modules
layer was 78 MB compressed through 0.1.6-alpha.1 and is ~137 MB from
alpha.2 on, so every future version would have failed the same way. The run
stayed green because a non-current version only warned.

Images are now exported from the daemon and pushed with regctl (pinned
v0.11.6, checksum-verified) in 50 MB chunks; the registry accepts chunked
uploads and a 140 MB layer went through in 12 s. Any version that fails to
build or push fails the run, as a final step so :latest and the releases for
everything that did ship are in place first. Manifests left untagged by
rebuilds are pruned each run, keeping anything a tagged index still points at.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2026-09-17 21:26:27 -04:00
Ward f7fbe495ab Mark the release :latest serves as the repository's latest release
Sync upstream and build images / sync-and-build (push) Successful in 32s
Gitea shows a repository's "latest release" on the home page and at
/releases/latest, and defines it as the newest non-pre-release. Upstream
flags every RC and alpha pre-release, and mirroring that left the home page
with no release listed although ten exist. The release for the version
:latest serves is now the one full release; every other version keeps
upstream's flag, and the flag is part of the up-to-date check so it flips
when :latest moves.
2026-09-11 14:30:48 -04:00
Ward 0070b82414 Inspect deployment: show processes and listening sockets 2026-09-11 14:13:39 -04:00
Ward 50121a390f Name colliding plugin layers at boot; note the 0.1.5 file-upload collision
Sync upstream and build images / sync-and-build (push) Successful in 2m18s
dsh 0.1.5 ships file upload under the loader id file-upload, which the
community dsh-file-upload plugin also registers. A profile that still carries
that plugin dies at boot with "duplicate loader entry id: file-upload" and the
stack trace never names the plugin. The entrypoint now compares the profile's
plugin layers (and the two hand-edited patch layers) against the installed
version's own bundle patches before every start and logs the offending plugin
with its remove command. Advisory only: the start is never blocked, and only
the profile manifest and patch YAML are read as data.

The 0.1.5+ release footer carries the caveat, and the publisher now re-renders
a release when its text changes rather than only when the digest does.
README: upgrade checklist covers this class of failure; the Highlights bullets
that still described the pre-mirror tag/release model are corrected.
2026-09-11 13:54:23 -04:00
Ward dfaa1a82fc Inspect deployment: show recent logs and how the state-volume profile is composed
Read-only probe for boot failures: collapsed log tail, the profile package.json and patch layers (key-like values redacted, credentials never read), profile-local bundle packages and the rows they insert. Tooling probe falls back to the image when the container is not running.
2026-09-11 13:42:26 -04:00
Miles WardandClaude Fable 5 a30d19c143 Skip upstream versions that cannot be built, with the reason recorded
Sync upstream and build images / sync-and-build (push) Successful in 30s
0.1.3-alpha.2 depends on the native module fs-ext, which needs a C toolchain
the slim runtime image does not carry; without a skip list the run would
retry it every six hours and warn each time. README notes the three
upstream tags that have no image and why.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 13:25:13 -04:00
Miles WardandClaude Fable 5 12dc406a2d Mirror upstream tag-for-tag: one image and one release per upstream version
Sync upstream and build images / sync-and-build (push) Successful in 12m13s
Tags and releases now look like https://github.com/deepseek-ai/deepseek-harness/tags:

- The mirror step pushes upstream's dsh-v* tags verbatim (same names, commits,
  dates), so the Tags page is upstream's.
- CI builds an image for every upstream version that npm published, from
  MIN_VERSION (0.1.1-rc.2) up, and REBUILDS IT IN PLACE when our recipe, pnpm,
  or the node base image moved since it was built. One image tag per version -
  no -rN revisions. A failing older version is reported and left on its
  previous image; a failing current version fails the run. A manual run can
  force-rebuild specific versions or "all".
- :latest = the newest release candidate (alphas never qualify), as a manifest
  copy with an identical digest.
- Releases: one per built version, ON the mirrored upstream tag, titled like
  upstream (v0.1.5-rc.2), carrying upstream's English notes plus a five-line
  container footer (digest, pnpm, base, build). Updated in place on rebuild,
  keeping previous digests in a collapsed history. Created oldest-first so the
  Releases page reads in version order.
- Removed my v*/v*-rN tags and releases; README rewritten for this model.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 13:11:12 -04:00
Miles WardandClaude Fable 5 76112eadf6 Track the newest release candidate: :latest alias, current-version freshness, on-demand revisions
Sync upstream and build images / sync-and-build (push) Successful in 2m4s
Operating model change, at the operator's request: the deployment follows
the newest release candidate rather than a hand-pinned version.

- :latest is now published, as a manifest copy (identical digest) of the
  newest build of the newest RC version. It is the ONLY tag that moves;
  every <version>[-rN] tag stays immutable for anyone who wants to pin.
- Freshness (pnpm / base image / recipe hash) now applies to the current
  version derived from the registry, not to a Dockerfile constant, so an
  image fix reaches the deployed line automatically. The `revise` dispatch
  input rebuilds any older versions with the current recipe on demand.
- Smoke test now runs on a fresh named volume (root-owned, like a new Unraid
  appdata dir) and asserts pid 1 is uid 1000 - the ownership fix is exercised
  on every build, not just locally.
- Releases: `latest` gets no release; the "pinned deployment" callout is gone
  (the README and the alias are the source of truth now).
- 0.1.1-rc.2 is deprecated; its -r2/-r49/-r52 revisions were removed
  (images, tags, releases). The original tag remains as history.
- README/template: :latest in the Repository field, auto-update warning,
  versions table and upgrade procedure rewritten for the new model.
  Forensic workflow's trusted default -> :latest. inspect-deployment default
  container name -> Deepseek.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 12:35:40 -04:00
Miles WardandClaude Fable 5 19e87258ce Releases: describe a revision accurately
Sync upstream and build images / sync-and-build (push) Successful in 25s
A -rN build is not necessarily a toolchain refresh — -r2 changed neither pnpm
nor the base image, only the container's own tooling. Say what is certain
(same upstream release, rebuilt recipe, safe to move to) and let the labels
and the linked commit carry the specifics.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 12:15:08 -04:00
Miles WardandClaude Fable 5 2a0c808149 Make provenance-label readers multi-arch aware
Sync upstream and build images / sync-and-build (push) Successful in 25s
Publishing -r2 exposed this: a buildx multi-arch push stores an OCI index at
the tag, and both label readers assumed a single-arch manifest. Consequences
were a release body with 'pnpm ?' and — much worse — CI's freshness check
seeing the pinned image as permanently stale, which would have published a
new -rN revision every six hours forever.

Both readers now follow an index to a platform manifest (preferring amd64,
the deployment's arch) before reading the config blob, and the publisher
warns when an image carries no provenance labels at all. Verified against
both a multi-arch tag (0.1.1-rc.2-r2) and a single-arch one (0.1.5-rc.2):
the pinned build now reads as current, so no redundant rebuild.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 12:11:25 -04:00
Miles WardandClaude Fable 5 400797ecac Wrap the pnpm family too, not just dsh
Sync upstream and build images / sync-and-build (push) Successful in 1m45s
Verifying the pushed -r1 image found one hole: only dsh was wrapped, so a
root 'docker exec <c> pnpm ...' created PNPM_HOME/XDG_CONFIG_HOME owned by
root inside the state volume — which the unprivileged dsh could not then use
for plugin installs (self-healing on the next restart, since the entrypoint
re-chowns, but broken until then).

The wrapper is now generic (execs the resolved target directly; dsh's bin.js
is +x with a node shebang and pnpm's bin is an ELF, so no node prefix is
needed) and is installed over dsh, pnpm, pnpx, pn and pnx. Renamed
dsh-wrapper.sh -> run-as-runtime-user.sh to match what it does.

Verified: root 'docker exec pnpm --version' + 'pnpm store path' + 'dsh plugin
add' leave zero root-owned files in the volume; 23-case matrix still green.
Ships as 0.1.1-rc.2-r2.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 12:08:54 -04:00
Miles WardandClaude Fable 5 acd81b2f79 Self-healing volume ownership: no more manual chown, no more EACCES
Sync upstream and build images / sync-and-build (push) Failing after 22s
A bind mount arrives with the host's ownership, so a freshly created appdata
directory killed dsh with EACCES on mkdir /home/node/.dsh/profiles/web. Only
root inside the container can fix that, so:

- docker-entrypoint.sh starts as root, aligns the node account with PUID/PGID
  (default 1000:1000, Unraid convention), takes ownership of the state volume
  when needed, then setpriv's down before exec'ing dsh. The network-reachable
  process is never root and no setuid binary is left behind. The workspace
  mount point is made writable but its CONTENTS are left alone unless
  DSH_CHOWN_WORKSPACE=1 — never silently rewrite ownership across user data.
- Dropped USER node (root is required to chown); dsh is wrapped so a root
   re-execs as PUID:PGID and cannot leave
  root-owned files in the volume. Wrapper resolves the real bin.js at build
  time and self-tests.
- Explicit --user is honored: ownership cannot be repaired, so the entrypoint
  verifies writability and exits with one actionable line instead of a Node
  stack trace.
- XDG_CONFIG_HOME pinned into the volume too; pnpm was reaching for
  /root/.config when HOME was unset.
- CI: labels now carry a recipe hash, and the freshness check cuts a -rN
  revision when the recipe changes — so an image fix reaches the pinned
  deployment without waiting for an upstream release.
- README/template: chown step removed, PUID/PGID/DSH_CHOWN_WORKSPACE
  documented, template pinned to 0.1.1-rc.2-r1.

Verified with a 23-case matrix: root-owned volume self-heals and serves 200,
already-correct volume skips the chown, 99:100 + PUID/PGID works, plugin
add/remove via root docker exec leaves no root-owned files, explicit --user
still works and fails clearly when it can't, both argument forms serve.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 12:00:30 -04:00
Miles WardandClaude Fable 5 d6a97dd8ea inspect-deployment: keep format strings inside the YAML block
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 11:42:58 -04:00
Miles WardandClaude Fable 5 95754b9124 Add manual inspect-deployment workflow (what is the Unraid container actually running?)
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 11:39:41 -04:00
Miles WardandClaude Fable 5 7a5e8a4ca0 Releases: mark the pinned deployment version automatically
Sync upstream and build images / sync-and-build (push) Successful in 25s
The generator now receives the Dockerfile's default DSH_VERSION and calls out
that release as the pinned deployment version, so the Releases page says
which tag the Unraid template runs without hand-editing.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-10 17:18:50 -04:00
Miles WardandClaude Fable 5 ca70b61844 Build npm next too; release generator with upstream notes + provenance; tags cleanup
Sync upstream and build images / sync-and-build (push) Successful in 2m35s
- CI plan: candidates are npm latest AND next (release candidates), plus an
  optional workflow_dispatch 'version' input for any exact version. This is
  what was holding back 0.1.5-rc.2 (published as next, not latest).
- Upstream dsh-v* tags are no longer mirrored into this repo (deleted the
  existing ones); master stays an exact branch mirror and every release links
  to its upstream tag. This repo's tags are now exclusively v<image tag>.
- Dockerfile: build-provenance labels (org.opencontainers.image.revision /
  .created, dev.milesward.ci-run) so releases cite the commit and CI run that
  actually built the image instead of whatever HEAD is at publish time.
- Releases: shell template replaced by .gitea/scripts/publish-releases.js,
  run on the node:22 base image (no runner deps). It pulls the upstream
  GitHub release's English notes, adds version-dependent caveats (launch
  token from 0.1.2, Session format V3 from 0.1.5), image facts and Unraid
  deploy steps. Existing v* tags re-pointed to 2d033c3 (the commit that
  built those images); their releases are regenerated in this format.
- README: dynamic badges (shields Gitea release, Gitea workflow status),
  0.1.5-rc.2 row, pipeline/tag-namespace docs updated.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-10 17:04:16 -04:00
Miles WardandClaude Fable 5 1a13239fb6 CI: build release body with printf (heredoc broke the YAML block scalar)
Sync upstream and build images / sync-and-build (push) Successful in 26s
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-10 15:39:33 -04:00
Miles WardandClaude Fable 5 5121d84b53 Transparent brand logo, per-tag releases, README rewrite, MIT license
- assets/: replace the white-background avatar with a transparent 512px
  PNG (brand blue #4d6bfe, tight-cropped) plus a 128px header variant.
  Same path, so the Unraid template icon keeps working.
- CI: every image tag in the registry now gets a v<tag> git tag on main
  and a Gitea release carrying digest, dsh/pnpm/base versions, deploy
  notes and the 0.1.2+ launch-token caveat. Idempotent, so it backfills
  the three existing tags on the next run.
- README rewritten to match OpenPXE/Discord-Authenticator: centered brand
  header, badges, contents, why/highlights, quick start, Unraid template,
  image contract, versions & upgrade procedure (0.1.5 session-format V3
  has no downgrade), plugins, security model, pipeline, layout.
- LICENSE: MIT for the build tooling.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-10 15:34:47 -04:00
Miles WardandClaude Fable 5 2d033c323b Post-incident hardening: pnpm, pinned versions, immutable tags, no more :latest
Sync upstream and build images / sync-and-build (push) Successful in 2m4s
- Dockerfile: install pnpm (pinned via PNPM_VERSION arg) alongside dsh so
  'dsh plugin --profile web add|update|remove' works in-container. Plain
  'npm i -g' instead of corepack: corepack shims resolve versions at runtime
  and write to a root-owned cache, breaking the uid-1000/read-only contract.
  All pnpm writable dirs (store via PNPM_HOME, cache/state via XDG vars)
  point into the /home/node/.dsh volume - verified zero rootfs writes.
- Dockerfile: ENTRYPOINT ['dsh'], CMD carries the patch flag AND --no-open
  (a headless server must not open a browser; on 0.1.2+ that can burn the
  one-time launch token). DSH_VERSION default pinned to 0.1.1-rc.2.
- CI: never publishes or moves 'latest' (deleted from the registry). Builds
  only missing immutable version tags; a toolchain freshness check rebuilds
  the pinned version as <ver>-rN when a newer pnpm or node base image ships.
  Every build passes a smoke test before push: dsh/pnpm versions match and
  the server boots with the baked patch, answering on the container's
  non-loopback interface (200 pre-token, 303/401 on 0.1.2+).
- README: operating contract - pinned version rationale, PostArgs-replace-CMD
  warning, 0.1.2 launch-token behavior, plugin management, lanmode
  incompatibility, template pinned to :0.1.1-rc.2.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-06 02:43:10 -04:00
Miles WardandClaude Fable 5 768de4ac75 Unraid template support: baked --patch layer, /workspace, logo, template in README
Sync upstream and build image / sync-and-build (push) Successful in 19s
- Ship /opt/deepseek-harness/web.cordis.patch.yml and pass it via upstream's
  --patch launcher flag (must precede the web command's own flags) instead of
  seeding DSH_HOME; entrypoint accepts template Post Arguments verbatim.
- Add /workspace (agent WORKDIR) and pin DSH_HOME=/home/node/.dsh; drop the
  implicit /home/node volume so templates mount .dsh and /workspace cleanly.
- README: Unraid template (repo/support/icon/versions corrected for this
  image, lanmode plugin caution), first-run chown note, DeepSeek logo.

Verified: no-args, flags-only, and full template invocations all serve the
web UI; trust fence still 403s unknown Hosts; state persists to mounted .dsh.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-02 03:07:45 -04:00
Miles WardandClaude Fable 5 f21287e5c0 Validation: separate dsh-own-code diff from dependency-tree noise
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-02 02:54:01 -04:00
Miles WardandClaude Fable 5 af0cacd06c Validation: correct suspect image name (runzhliu), report containers using it
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-02 02:51:36 -04:00
Miles WardandClaude Fable 5 b20949fcc9 Use scoped REGISTRY_TOKEN for registry auth; add suspect-image validation workflow
Sync upstream and build image / sync-and-build (push) Successful in 2m18s
The Actions GITHUB_TOKEN can push git refs but the container registry
rejects it (run 1 failure). Also adds a manual, never-executes-the-image
forensic workflow comparing a third-party image on the runner host's
daemon against the trusted build.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-02 02:47:42 -04:00
Miles WardandClaude Fable 5 94869972ea Bind via upstream profile patch layer instead of container IP
Sync upstream and build image / sync-and-build (push) Successful in 19s
dsh's webserver schema only accepts 127.0.0.1 or 0.0.0.0; the CLI refuses
0.0.0.0 by design. Seed $DSH_HOME/profiles/web/cordis.patch.yml (the
supported override) from the entrypoint instead. Verified: UI serves 200
through a published port, /api trust fence 403s unknown Hosts, and
--trusted-host pass-through lifts it.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-02 02:44:37 -04:00
Miles WardandClaude Fable 5 ca31308799 CI: mirror upstream and build trusted dsh container image
Sync upstream and build image / sync-and-build (push) Failing after 1m39s
Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-02 02:35:03 -04:00