Deploy Gatsby site from k3s staging hostname #70

Open
opened 2026-07-09 22:49:57 +00:00 by coilyco-ops · 11 comments
Collaborator

Goal

Add a k3s-hosted deployment path for this Gatsby site so iteration can happen from the homelab instead of waiting on Netlify. Use website.coilysiren.me as the provisional staging hostname unless a stronger existing convention appears during implementation. Keep coilysiren.me on Netlify for now.

Context already checked

  • README.md and docs/FEATURES.md currently document Netlify as the deploy path.
  • /substrate/infrastructure/docs/k3s-deploy-notes.md is the authoritative k3s reference. Its first-time checklist says a new app repo owns Dockerfile, deploy manifest, workflow, and config.yml.
  • Public *.coilysiren.me traffic should use Traefik Ingress on k3s. Host Caddy is tailnet shortcut glue, not the public path.
  • The canonical Ingress uses ingressClassName: traefik and cert-manager.io/cluster-issuer: letsencrypt-production.

Implementation sketch

  • Add a production container for the static Gatsby build. It should build with the existing repo commands and serve the built public/ output on $PORT or port 80.
  • Add the repo-local deploy config expected by the k3s deploy notes: config.yml, deploy/main.yml, and the Makefile or ward-compatible equivalent the canonical workflow expects.
  • Add a publish and deploy workflow that builds the image, pushes it, joins the tailnet, writes kubeconfig from secrets, runs the deploy target, and waits for rollout. Prefer the current canonical shape in /substrate/infrastructure/docs/k3s-deploy-notes-gha-workflow*.md.
  • Update README.md and docs/FEATURES.md so the deploy inventory says Netlify remains apex production while k3s serves the staging/iteration hostname.
  • Add or update tests/smoke checks so the static container serves the Gatsby output successfully.

Acceptance criteria

  • website.coilysiren.me is the staged k3s hostname in tracked config.
  • The site can still build locally through the repo ward verbs.
  • The k3s deploy workflow is committed and documented.
  • Netlify apex deploy behavior is not removed unless Kai explicitly asks for cutover.
  • The implementation lands through the normal direct-main flow for this Forgejo repo.

Follow-up that may be needed outside code

If the Route 53 A record or repo secrets are missing, add an issue comment with the exact missing external setup and keep all code changes landed. The k3s checklist names the record and secret set.

## Goal Add a k3s-hosted deployment path for this Gatsby site so iteration can happen from the homelab instead of waiting on Netlify. Use `website.coilysiren.me` as the provisional staging hostname unless a stronger existing convention appears during implementation. Keep `coilysiren.me` on Netlify for now. ## Context already checked * `README.md` and `docs/FEATURES.md` currently document Netlify as the deploy path. * `/substrate/infrastructure/docs/k3s-deploy-notes.md` is the authoritative k3s reference. Its first-time checklist says a new app repo owns `Dockerfile`, deploy manifest, workflow, and `config.yml`. * Public `*.coilysiren.me` traffic should use Traefik Ingress on k3s. Host Caddy is tailnet shortcut glue, not the public path. * The canonical Ingress uses `ingressClassName: traefik` and `cert-manager.io/cluster-issuer: letsencrypt-production`. ## Implementation sketch * Add a production container for the static Gatsby build. It should build with the existing repo commands and serve the built `public/` output on `$PORT` or port 80. * Add the repo-local deploy config expected by the k3s deploy notes: `config.yml`, `deploy/main.yml`, and the Makefile or ward-compatible equivalent the canonical workflow expects. * Add a publish and deploy workflow that builds the image, pushes it, joins the tailnet, writes kubeconfig from secrets, runs the deploy target, and waits for rollout. Prefer the current canonical shape in `/substrate/infrastructure/docs/k3s-deploy-notes-gha-workflow*.md`. * Update `README.md` and `docs/FEATURES.md` so the deploy inventory says Netlify remains apex production while k3s serves the staging/iteration hostname. * Add or update tests/smoke checks so the static container serves the Gatsby output successfully. ## Acceptance criteria * `website.coilysiren.me` is the staged k3s hostname in tracked config. * The site can still build locally through the repo ward verbs. * The k3s deploy workflow is committed and documented. * Netlify apex deploy behavior is not removed unless Kai explicitly asks for cutover. * The implementation lands through the normal direct-main flow for this Forgejo repo. ## Follow-up that may be needed outside code If the Route 53 A record or repo secrets are missing, add an issue comment with the exact missing external setup and keep all code changes landed. The k3s checklist names the record and secret set.
Author
Collaborator

WARD-RESERVATION: held 🔒

reservation details

Holder: container engineer-codex-website-70 on host kais-macbook-pro-2.local.

Reserved by ward agent --harness codex (reserved 2026-07-09T22:50:06Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (1h TTL). --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilysiren/website#70 · branch issue-70 · harness codex · workflow direct-main
  • Run: engineer-codex-website-70 · ward v0.540.0 · dispatched 2026-07-09T22:50:06Z
  • Comment thread: 0 included in the pre-flight read, 0 stripped (ward's own automated comments).

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.540.0).

— Codex, via ward agent

<!-- ward-agent-reservation --> WARD-RESERVATION: held 🔒 <details><summary>reservation details</summary> Holder: container `engineer-codex-website-70` on host `kais-macbook-pro-2.local`. Reserved by `ward agent --harness codex` (reserved 2026-07-09T22:50:06Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (1h TTL). `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh**. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilysiren/website#70` · branch `issue-70` · harness `codex` · workflow `direct-main` - **Run:** `engineer-codex-website-70` · ward `v0.540.0` · dispatched `2026-07-09T22:50:06Z` - **Comment thread:** 0 included in the pre-flight read, 0 stripped (ward's own automated comments). Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.540.0). </details> </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Collaborator

WARD-RESERVATION: released 🛑

release details

Run never started. ward container reap released container engineer-codex-website-70 (--harness codex): it exited without launching the agent (smoke-test death, ward#222/#264/#595), so it did no work and the hold it took is retracted. Nothing is running on this issue. It needs re-dispatch. A ward agent director re-queues it automatically. A manual ward agent retry no longer needs --force.

— Codex, via ward agent

<!-- ward-agent-reservation-released --> <!-- ward-needs-redispatch --> WARD-RESERVATION: released 🛑 <details><summary>release details</summary> Run never started. `ward container reap` released container `engineer-codex-website-70` (`--harness codex`): it exited without launching the agent (smoke-test death, ward#222/#264/#595), so it did no work and the hold it took is retracted. Nothing is running on this issue. It needs re-dispatch. A `ward agent director` re-queues it automatically. A manual `ward agent` retry no longer needs `--force`. </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Collaborator

WARD-DISPATCH: failed

failure details

This forwarded dispatch failed after the issue was already reserved.

Attempted harness: codex
Attempted run: ward agent engineer coilysiren/website#70 --harness codex
Container: engineer-codex-website-70
Container created: no running engineer was observed.
Host log: /Users/kai/.ward/agent-logs/dispatch/20260709T225001Z-director-codex-hd68-coilysiren-website-70.log
Failure: ward: no GitHub token found - set GITHUB_TOKEN (or GH_TOKEN / WARD_GITHUB_TOKEN) to a token with repo scope, or set WARD_GITHUB_TOKEN_SOURCE=gh to mint one from your gh login; ward reads GitHub tokens only from the environment, never SSM (ward#489). See docs/agent-github.md

Retry: choose another harness if the first one is down, or rerun with --force if the reservation is stale.

— Codex, via ward agent

<!-- ward-agent-reservation-released --> <!-- ward-needs-redispatch --> WARD-DISPATCH: failed ❌ <details><summary>failure details</summary> This forwarded dispatch failed after the issue was already reserved. Attempted harness: `codex` Attempted run: `ward agent engineer coilysiren/website#70 --harness codex` Container: `engineer-codex-website-70` Container created: no running engineer was observed. Host log: `/Users/kai/.ward/agent-logs/dispatch/20260709T225001Z-director-codex-hd68-coilysiren-website-70.log` Failure: `ward: no GitHub token found - set GITHUB_TOKEN (or GH_TOKEN / WARD_GITHUB_TOKEN) to a token with repo scope, or set WARD_GITHUB_TOKEN_SOURCE=gh to mint one from your `gh` login; ward reads GitHub tokens only from the environment, never SSM (ward#489). See docs/agent-github.md` Retry: choose another harness if the first one is down, or rerun with `--force` if the reservation is stale. </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Collaborator

WARD-RESERVATION: held 🔒

reservation details

Holder: container engineer-codex-website-70 on host kais-macbook-pro-2.local.

Reserved by ward agent --harness codex (reserved 2026-07-09T22:50:44Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (1h TTL). --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilysiren/website#70 · branch issue-70 · harness codex · workflow direct-main
  • Run: engineer-codex-website-70 · ward v0.540.0 · dispatched 2026-07-09T22:50:44Z
  • Comment thread: 2 included in the pre-flight read, 1 stripped (ward's own automated comments).

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.540.0).

— Codex, via ward agent

<!-- ward-agent-reservation --> WARD-RESERVATION: held 🔒 <details><summary>reservation details</summary> Holder: container `engineer-codex-website-70` on host `kais-macbook-pro-2.local`. Reserved by `ward agent --harness codex` (reserved 2026-07-09T22:50:44Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (1h TTL). `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh**. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilysiren/website#70` · branch `issue-70` · harness `codex` · workflow `direct-main` - **Run:** `engineer-codex-website-70` · ward `v0.540.0` · dispatched `2026-07-09T22:50:44Z` - **Comment thread:** 2 included in the pre-flight read, 1 stripped (ward's own automated comments). - included: @coilyco-ops (2026-07-09T22:50:25Z), @coilyco-ops (2026-07-09T22:50:27Z) - stripped: @coilyco-ops (2026-07-09T22:50:08Z) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.540.0). </details> </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Collaborator

WARD-RESERVATION: released 🛑

release details

Run never started. ward container reap released container engineer-codex-website-70 (--harness codex): it exited without launching the agent (smoke-test death, ward#222/#264/#595), so it did no work and the hold it took is retracted. Nothing is running on this issue. It needs re-dispatch. A ward agent director re-queues it automatically. A manual ward agent retry no longer needs --force.

— Codex, via ward agent

<!-- ward-agent-reservation-released --> <!-- ward-needs-redispatch --> WARD-RESERVATION: released 🛑 <details><summary>release details</summary> Run never started. `ward container reap` released container `engineer-codex-website-70` (`--harness codex`): it exited without launching the agent (smoke-test death, ward#222/#264/#595), so it did no work and the hold it took is retracted. Nothing is running on this issue. It needs re-dispatch. A `ward agent director` re-queues it automatically. A manual `ward agent` retry no longer needs `--force`. </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Collaborator

WARD-DISPATCH: failed

failure details

This forwarded dispatch failed after the issue was already reserved.

Attempted harness: codex
Attempted run: ward agent engineer coilysiren/website#70 --harness codex
Container: engineer-codex-website-70
Container created: no running engineer was observed.
Host log: /Users/kai/.ward/agent-logs/dispatch/20260709T225039Z-director-codex-hd68-coilysiren-website-70.log
Failure: ward: no GitHub token found - set GITHUB_TOKEN (or GH_TOKEN / WARD_GITHUB_TOKEN) to a token with repo scope, or set WARD_GITHUB_TOKEN_SOURCE=gh to mint one from your gh login; ward reads GitHub tokens only from the environment, never SSM (ward#489). See docs/agent-github.md

Retry: choose another harness if the first one is down, or rerun with --force if the reservation is stale.

— Codex, via ward agent

<!-- ward-agent-reservation-released --> <!-- ward-needs-redispatch --> WARD-DISPATCH: failed ❌ <details><summary>failure details</summary> This forwarded dispatch failed after the issue was already reserved. Attempted harness: `codex` Attempted run: `ward agent engineer coilysiren/website#70 --harness codex` Container: `engineer-codex-website-70` Container created: no running engineer was observed. Host log: `/Users/kai/.ward/agent-logs/dispatch/20260709T225039Z-director-codex-hd68-coilysiren-website-70.log` Failure: `ward: no GitHub token found - set GITHUB_TOKEN (or GH_TOKEN / WARD_GITHUB_TOKEN) to a token with repo scope, or set WARD_GITHUB_TOKEN_SOURCE=gh to mint one from your `gh` login; ward reads GitHub tokens only from the environment, never SSM (ward#489). See docs/agent-github.md` Retry: choose another harness if the first one is down, or rerun with `--force` if the reservation is stale. </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Collaborator

WARD-RESERVATION: held 🔒

reservation details

Holder: container engineer-codex-website-70 on host kais-macbook-pro-2.local.

Reserved by ward agent --harness codex (reserved 2026-07-09T22:51:22Z). Concurrent ward agent runs are blocked until it finishes or the reservation goes stale (1h TTL). --force overrides.

Do not comment on or edit this issue to steer the run while it is reserved. The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a new issue, dispatched fresh. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494).

run seed context — what this run is carrying (ward#609)
  • Resolved: coilysiren/website#70 · branch issue-70 · harness codex · workflow direct-main
  • Run: engineer-codex-website-70 · ward v0.540.0 · dispatched 2026-07-09T22:51:22Z
  • Comment thread: 4 included in the pre-flight read, 2 stripped (ward's own automated comments).

Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.540.0).

— Codex, via ward agent

<!-- ward-agent-reservation --> WARD-RESERVATION: held 🔒 <details><summary>reservation details</summary> Holder: container `engineer-codex-website-70` on host `kais-macbook-pro-2.local`. Reserved by `ward agent --harness codex` (reserved 2026-07-09T22:51:22Z). Concurrent `ward agent` runs are blocked until it finishes or the reservation goes stale (1h TTL). `--force` overrides. **Do not comment on or edit this issue to steer the run while it is reserved.** The engineer seeded the body once at launch and never re-reads it, so a comment or edit reaches only human readers, never the running engineer. A correction goes to a **new issue, dispatched fresh**. That is the only channel that reaches a run in flight. Where the forge supports it, ward locks this conversation to make that a road-block rather than a convention (ward#494). <details><summary>run seed context — what this run is carrying (ward#609)</summary> - **Resolved:** `coilysiren/website#70` · branch `issue-70` · harness `codex` · workflow `direct-main` - **Run:** `engineer-codex-website-70` · ward `v0.540.0` · dispatched `2026-07-09T22:51:22Z` - **Comment thread:** 4 included in the pre-flight read, 2 stripped (ward's own automated comments). - included: @coilyco-ops (2026-07-09T22:50:25Z), @coilyco-ops (2026-07-09T22:50:27Z), @coilyco-ops (2026-07-09T22:51:02Z), @coilyco-ops (2026-07-09T22:51:03Z) - stripped: @coilyco-ops (2026-07-09T22:50:08Z), @coilyco-ops (2026-07-09T22:50:46Z) Static container doctrine and seed boilerplate are identical every run and omitted here (they ride ward v0.540.0). </details> </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Collaborator

WARD-RESERVATION: released 🛑

release details

Run never started. ward container reap released container engineer-codex-website-70 (--harness codex): it exited without launching the agent (smoke-test death, ward#222/#264/#595), so it did no work and the hold it took is retracted. Nothing is running on this issue. It needs re-dispatch. A ward agent director re-queues it automatically. A manual ward agent retry no longer needs --force.

— Codex, via ward agent

<!-- ward-agent-reservation-released --> <!-- ward-needs-redispatch --> WARD-RESERVATION: released 🛑 <details><summary>release details</summary> Run never started. `ward container reap` released container `engineer-codex-website-70` (`--harness codex`): it exited without launching the agent (smoke-test death, ward#222/#264/#595), so it did no work and the hold it took is retracted. Nothing is running on this issue. It needs re-dispatch. A `ward agent director` re-queues it automatically. A manual `ward agent` retry no longer needs `--force`. </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Collaborator

WARD-DISPATCH: failed

failure details

This forwarded dispatch failed after the issue was already reserved.

Attempted harness: codex
Attempted run: ward agent engineer coilysiren/website#70 --harness codex
Container: engineer-codex-website-70
Container created: no running engineer was observed.
Host log: /Users/kai/.ward/agent-logs/dispatch/20260709T225117Z-director-codex-hd68-coilysiren-website-70.log
Failure: ward: no GitHub token found - set GITHUB_TOKEN (or GH_TOKEN / WARD_GITHUB_TOKEN) to a token with repo scope, or set WARD_GITHUB_TOKEN_SOURCE=gh to mint one from your gh login; ward reads GitHub tokens only from the environment, never SSM (ward#489). See docs/agent-github.md

Retry: choose another harness if the first one is down, or rerun with --force if the reservation is stale.

— Codex, via ward agent

<!-- ward-agent-reservation-released --> <!-- ward-needs-redispatch --> WARD-DISPATCH: failed ❌ <details><summary>failure details</summary> This forwarded dispatch failed after the issue was already reserved. Attempted harness: `codex` Attempted run: `ward agent engineer coilysiren/website#70 --harness codex` Container: `engineer-codex-website-70` Container created: no running engineer was observed. Host log: `/Users/kai/.ward/agent-logs/dispatch/20260709T225117Z-director-codex-hd68-coilysiren-website-70.log` Failure: `ward: no GitHub token found - set GITHUB_TOKEN (or GH_TOKEN / WARD_GITHUB_TOKEN) to a token with repo scope, or set WARD_GITHUB_TOKEN_SOURCE=gh to mint one from your `gh` login; ward reads GitHub tokens only from the environment, never SSM (ward#489). See docs/agent-github.md` Retry: choose another harness if the first one is down, or rerun with `--force` if the reservation is stale. </details> <!-- ward-agent-signature --> — Codex, via `ward agent`
Author
Collaborator

Director note, 2026-07-09: the read-only surface filed this from Kai’s request to host an iterable k3s version of the website. Two dispatch attempts were made from /workspace/website:

  • warded engineer coilysiren/website#70 --harness codex
  • warded engineer #70 --harness codex\n* warded engineer #70\n\nAll stopped before launching with the same broker preflight: no GitHub token found - set GITHUB_TOKEN (or GH_TOKEN / WARD_GITHUB_TOKEN) to a token with repo scope, or set WARD_GITHUB_TOKEN_SOURCE=gh to mint one from your gh login. The issue is ready for a sibling engineer once the dispatcher has the GitHub-lane token source it currently expects for this repo.
Director note, 2026-07-09: the read-only surface filed this from Kai’s request to host an iterable k3s version of the website. Two dispatch attempts were made from `/workspace/website`: * `warded engineer coilysiren/website#70 --harness codex` * `warded engineer #70 --harness codex`\n* `warded engineer #70`\n\nAll stopped before launching with the same broker preflight: `no GitHub token found - set GITHUB_TOKEN (or GH_TOKEN / WARD_GITHUB_TOKEN) to a token with repo scope, or set WARD_GITHUB_TOKEN_SOURCE=gh to mint one from your gh login`. The issue is ready for a sibling engineer once the dispatcher has the GitHub-lane token source it currently expects for this repo.
Author
Collaborator

Formatting correction for the dispatch note above. These were the attempted dispatch commands:

  • warded engineer coilysiren/website#70 --harness codex
  • warded engineer "#70" --harness codex
  • warded engineer "#70"

Each attempt stopped before launching with the same broker preflight: no GitHub token found. The captured implementation issue is still ready to run once the dispatcher has a GitHub token source for this repo.

Formatting correction for the dispatch note above. These were the attempted dispatch commands: * `warded engineer coilysiren/website#70 --harness codex` * `warded engineer "#70" --harness codex` * `warded engineer "#70"` Each attempt stopped before launching with the same broker preflight: `no GitHub token found`. The captured implementation issue is still ready to run once the dispatcher has a GitHub token source for this repo.
Sign in to join this conversation.
No description provided.