Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 41 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,52 @@ These rules apply before the writing-style guidance below.
- Blog and Pulse broker streams are display/projection contracts only. Do not treat them as public Fediverse delivery, and do not weaken broker hydration tests just because prerendered fallback still paints.
- `static/cv` is synced from the private `spear_resumes` repository. Preserve that boundary and use the existing sync/test path instead of hand-editing generated resume artifacts.

## Cross-Repo Delivery Ownership

- This repo owns blog source, the static build, source-image/digest publication
and protected dispatch, and the production Cloudflare Pages contract.
- `Jesssullivan/jesssullivan-infra` owns the private tailnet acceptance
environment: dispatch validation and digest validation, private image
mirroring, RustFS-backed OpenTofu state, the `jesssullivan-blog-shadow`
workload, protected apply workflow, and Tailscale route.
- `https://jesssullivan-blog-shadow.taila4c78d.ts.net` is the approved active
exact-head acceptance and interactive QA route. Despite its stale name,
`.github/workflows/cloudflare-pages-shadow.yml` publishes the production
`transscendsurvival-org` Pages project on eligible `main` or manual runs;
pull-request runs are build-only. `https://tss.ephemera.tinyland.dev` and
the separately deployed `https://tss.tinyland.dev` still serve legacy
compatibility content, but both are retired as QA routes. Neither proves the
digest-pinned private exact-head route. Cloudflare Pages remains production
serving.
- `tinyland-inc/GloriousFlywheel` supplies reusable runner/build, Nix/toolchain,
Bazel/cache/RBE, enrollment, and validation substrate. Passing GF checks or
running on GF substrate does not transfer application deployment ownership
to GF.
- `tinyland-inc/tinyland.dev` owns mothership content, broker, producer, and
federation contracts consumed by this spoke.
- `tinyland-inc/blahaj` is the bounded infrastructure receiver and owns
cluster-side admission, RBAC, placement, storage,
DNS/certificate/tunnel enforcement, and state contracts. Its app-specific
receivers and reapers are limited to the canonical adopted-live exception
register; none owns this blog's shadow workload, apply decision, or
application lifecycle. `tinyland-inc/lab` may bootstrap hosts, enforce
runtime policy and operator preflights, and project narrowly scoped
credentials, but it is not an application deployment owner.
- Never infer application ownership from the cluster hosting a pod, the repo
escrowing a credential, or the runner executing a build. Follow the
source-to-digest-to-overlay chain above.

## Build, Test, And Deploy

- Normal local development is npm/SvelteKit: `npm ci`, `npm run build`, `npm run lint`, and focused scripts from `package.json`.
- Production behavior changes require `npm run test:production-health`. That check covers public DNS, apex/`www` HTTPS, canonical redirects, slash variants, Tinyland broker contract, and browser hydration.
- CI has two lanes. `build-and-test` runs hosted checks such as gitleaks, production dependency audit, lint, npm build, bundle reporting, and Lighthouse. `bazel-remote-gates` is the check/test/e2e authority.
- Cloudflare Pages shadow builds come from `.github/workflows/cloudflare-pages-shadow.yml`; PRs are build-only unless the workflow is explicitly eligible to deploy. GitHub Pages deploys come from `.github/workflows/deploy-pages.yml` and are still needed for rollback parity.
- `.github/workflows/cloudflare-pages-shadow.yml` is the stale-named
Cloudflare Pages production build/deploy lane; pull requests build without
deploying. It does not make either legacy `tss` route an active QA surface
and is not the private exact-head acceptance authority. GitHub Pages deploys
come from `.github/workflows/deploy-pages.yml` and are still needed for
rollback parity.
- `.github/workflows/production-health.yml` runs every 30 minutes and sends ntfy alerts on failure. Treat a red scheduled monitor as production evidence, not noise.

## Bazel And GloriousFlywheel
Expand Down
151 changes: 94 additions & 57 deletions docs/blog-shadow-preview.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,54 @@
# Blog Shadow Preview

The live shadow preview route is owned by `Jesssullivan/jesssullivan-infra`,
but this repo owns the branch build that feeds it.
The approved active development and QA surface is the private exact-head
tailnet route. `Jesssullivan/jesssullivan-infra` owns that acceptance
environment; this repo owns the source and static build that feeds it.

Current review route:
Approved development and QA route:

```text
https://jesssullivan-blog-shadow.taila4c78d.ts.net
```

## Ownership

- `Jesssullivan/jesssullivan.github.io` owns blog source, the static build,
source-image/digest publication, protected dispatch, and the Cloudflare Pages
production contract.
- `Jesssullivan/jesssullivan-infra` validates the dispatched source evidence
and digest, mirrors that digest privately, and owns the RustFS-backed
OpenTofu state, workload, apply, and private Tailscale route.
- GloriousFlywheel supplies reusable runner/build, Nix/toolchain,
Bazel/cache/RBE, enrollment, and validation substrate. It does not own this
application's deployment.
- Blahaj and Lab do not own this application. Blahaj is the bounded
infrastructure receiver for cluster admission, RBAC, placement, storage,
DNS/certificate/tunnel enforcement, and state contracts; its canonical
adopted-live receiver/reaper exceptions do not transfer source, workload,
route, apply-decision, or lifecycle authority. Neither do Lab host bootstrap,
runtime policy, operator preflight, or scoped credential projection.

## Automatic PR Flow

`.github/workflows/shadow-preview.yml` publishes the newest active same-repo PR
branch to the shared shadow route.
`.github/workflows/shadow-preview.yml` is the automatic exact-head lane for the
newest active same-repo PR branch on the shared shadow route.

1. A same-repo, non-draft PR against `main` is opened, marked ready, or updated.
2. The workflow builds `Dockerfile.shadow` on the configured source runner.
`BLOG_SHADOW_SOURCE_RUNNER` is currently `ubuntu-latest`; manual dispatch
retains `tinyland-dind` as its explicit default.
2. The workflow builds the static output through `Dockerfile.shadow` on the
configured source runner. `BLOG_SHADOW_SOURCE_RUNNER` is currently
`ubuntu-latest`; manual dispatch retains `tinyland-dind` as its explicit
default.
3. The workflow pushes the CI source artifact to
`ghcr.io/jesssullivan/jesssullivan-github-io-shadow-tailnet`.
`ghcr.io/jesssullivan/jesssullivan-github-io-shadow-tailnet` and publishes
the immutable OCI source digest as a job output.
4. A separate, no-checkout job enters the reviewer-gated
`blog-shadow-dispatch` environment, mints a short-lived GitHub App token,
and starts the named workflow in `Jesssullivan/jesssullivan-infra` with the
exact image digest and source metadata.
5. The private infra workflow mirrors that digest into the private operator
package and applies the RustFS-backed OpenTofu stack.
source repository, workflow run, commit SHA, correlation tag, and exact
source digest.
5. The private infra workflow validates that source workflow evidence and the
tag-to-digest binding, mirrors the exact digest into the private operator
package, and applies the RustFS-backed OpenTofu stack.
6. The public workflow follows the source-SHA/run-ID-correlated private run
and fails unless that exact receiver run succeeds.

Expand All @@ -35,6 +58,12 @@ private dispatches use separate `blog-shadow-preview-build` and
true`. The private receiver also serializes applies, so the newest approved PR
wins without draft resolve-only runs canceling an in-flight deploy.

The correlated receiver run proves the digest-pinned workload and canonical
service responses through a Kubernetes API port-forward, and requires the
Tailscale Ingress to report a MagicDNS hostname. Direct HTTPS against the
MagicDNS route is an independent tailnet-route QA check from a
tailnet-connected client. Neither proof substitutes for the other.

Fork PRs and draft PRs are ignored. Branch pushes are covered by the PR
`synchronize` event so the workflow does not create duplicate push and PR check
runs for the same commit.
Expand Down Expand Up @@ -65,51 +94,58 @@ and is currently `ubuntu-latest`. Manual dispatch can select `tinyland-dind`
when an ARC source-build proof is required. A hosted build does not prove the
ARC source-build lane.
The correlated receiver result still separately proves private GHCR mirroring,
RustFS-backed OpenTofu apply, and tailnet smoke.

## Manual Shadow Image Build

`.github/workflows/shadow-image.yml` still supports the older
`shadow-deploy/**` branch flow for explicit operator builds. That workflow only
builds the source image; the private mirror and apply are handled by infra. It
uses the `tinyland-dind` ARC runner by default and accepts the same
`BLOG_SHADOW_SOURCE_RUNNER=ubuntu-latest` fallback, or manual dispatch
`source_runner=ubuntu-latest`, when the ARC source-image lane is unavailable.

## Cloudflare Pages Shadow

`.github/workflows/cloudflare-pages-shadow.yml` builds the same static
SvelteKit output and can publish it to Cloudflare Pages by Direct Upload.

This workflow exercises a separate Cloudflare Pages shadow without changing
production DNS. `https://transscendsurvival.org` is served by Cloudflare Pages;
`https://tss.tinyland.dev` remains the review shadow. The built site is still
static, but current `/blog`,
`/blog/[slug]`, and `/pulse` client code may hydrate from public
`hub.tinyland.dev` broker endpoints at runtime when those endpoints are
available.
RustFS-backed OpenTofu apply, exact-head runtime attestation, canonical service
smoke, and Tailscale Ingress status. Direct MagicDNS HTTPS remains the
independent tailnet-route QA proof described above.

## Legacy Manual Shadow Image Build

`.github/workflows/shadow-image.yml` is a legacy/manual, tag-correlated source
build for `shadow-deploy/**` branches or explicit workflow dispatch. It pushes
a tagged source image only. It does not dispatch
`Jesssullivan/jesssullivan-infra`, and it is not the automatic exact-head lane.

An operator using this legacy lane must resolve the published tag to an
immutable source digest, mirror that exact digest into the private operator
package with infra-owned authority, and deliberately apply the infra-owned
stack with the digest, source SHA, and source-run correlation. The workflow
uses the `tinyland-dind` ARC runner by default and accepts
`BLOG_SHADOW_SOURCE_RUNNER=ubuntu-latest`, or manual dispatch with
`source_runner=ubuntu-latest`, when the ARC source-build lane is unavailable.

## Cloudflare Pages Production And Legacy Routes

Despite its stale name, `.github/workflows/cloudflare-pages-shadow.yml` builds
and, on eligible `main` or manual runs, deploys the production
`transscendsurvival-org` Pages project. Pull-request runs are build-only.
`https://tss.ephemera.tinyland.dev` still serves legacy compatibility content
from that project, while `https://tss.tinyland.dev` still serves the separate,
stale `tss-shadow` project. Both URLs are retired as QA routes. Do not
deliberately redeploy the separate `tss-shadow` project as part of blog QA.
None of these surfaces proves the private mirror, infra-owned state/apply,
digest-pinned workload, or current MagicDNS route, so do not cite them as
exact-head acceptance evidence.

The workflow uses these repository credentials and configuration:

Required repository secrets:

| Secret | Purpose |
|---|---|
| `CLOUDFLARE_ACCOUNT_ID` | Cloudflare account that owns the Pages project |
| `CLOUDFLARE_API_TOKEN` | Token with Cloudflare Pages edit/deploy access |

Optional repository variable:

| Variable | Default | Purpose |
| Kind | Name | Purpose |
|---|---|---|
| `CLOUDFLARE_PAGES_PROJECT_NAME` | `transscendsurvival-org` | Cloudflare Pages project name |

PRs and missing-credential runs build and validate the static site, then skip
the Cloudflare deploy with an explicit notice. Manual dispatch accepts
`require_deploy=true` when the operator wants missing credentials to fail
instead of skip.

Cloudflare Pages is the production serving authority. GitHub Pages remains the
rollback publisher and parity path. Browser validation remains in GitHub
Actions or an approved remote lane. Do not run local Playwright for this slice.
| Secret | `CLOUDFLARE_ACCOUNT_ID` | Cloudflare account that owns the Pages project |
| Secret | `CLOUDFLARE_API_TOKEN` | Token with Cloudflare Pages edit/deploy access |
| Variable | `CLOUDFLARE_PAGES_PROJECT_NAME` | Pages project name; defaults to `transscendsurvival-org` |

PRs and missing-credential runs build and validate the static artifact, then
skip the Cloudflare deploy with an explicit notice. Eligible `main` pushes and
manual runs can deploy production. Manual dispatch accepts
`require_deploy=true` when an operator deliberately wants missing credentials
to fail instead of skip. This is production-serving machinery, not the
interactive PR QA route.

Cloudflare Pages remains the production serving authority for
`https://transscendsurvival.org` and `https://www.transscendsurvival.org`.
GitHub Pages remains the rollback publisher and parity path. The approved
exact-head acceptance and interactive QA target is the private tailnet route
documented above.

## Pulse Client Smoke

Expand All @@ -130,5 +166,6 @@ curl -fsSIL "$SHADOW/pulse/client"
curl -fsSL "$SHADOW/data/pulse/public-snapshot.v1.json"
```

Browser validation for the client route stays in hosted GitHub Actions. Do not
run Playwright locally.
Browser validation for the client route stays in the GF-backed
`bazel-remote-gates` GitHub Actions job on `tinyland-dind`. Do not run
Playwright locally.
Loading