design(docs): architecture for docs.cl8y.com static host #1

Closed
opened 2026-09-06 12:15:20 +00:00 by ops-worker · 5 comments
Member

Summary

Write the architecture for code/cl8y-docs — the first-party static host for https://docs.cl8y.com. This issue is design only. Do not scaffold Vite, Docker, nginx, or page copy here.

Parent / source intent: PlasticDigits/cl8y-marketing#3. Repo create is done (marketing#6, closed). Governance stays in PlasticDigits/cl8y-marketing.

Sequence for this empty repo (do not skip):

  1. This issue — architecture document on main.
  2. #2 — program review of that document (gated on #1).
  3. #3 — one bundled v0 implement (gated on #2 ACCEPT). Do not apply ready / agent:implement to #3 until then.

Current codebase

This repository is README-only (main @ initial commit). There is no package.json, Vite config, Dockerfile, nginx.conf, public/robots.txt, public/sitemap.xml, Woodpecker pipeline, prerender, or route modules. docs.cl8y.com is not a shipped first-party static site (TLS is not a valid product cert).

This repo (code/cl8y-docs)

  • README.md — one paragraph: TypeScript static site for docs.cl8y.com (Vite + React + TS → nginx), Coolify-deployable; governance in PlasticDigits/cl8y-marketing.
  • No application source. Nothing from marketing#3 is implemented here.

Governance (PlasticDigits/cl8y-marketing)

  • marketing#3 is the product spec: one deployable = one repo, Node 24 builder, nginx non-root, honest crawl files, unique prerendered titles/canonicals, stub indexable routes, DEX CTAs via URLSearchParams, no unverified claims.
  • strategy/technical-architecture.md — TypeScript + React static sites, Vite → nginx, one repo per deployable, public VITE_* only.
  • strategy/operating-system.md — crawlable layer separate from the transaction SPA (robots.txt / sitemap.xml with correct types; prerendered unique titles).
  • strategy/90-day-plan.md Phase 1 — unique canonicals on indexable routes; do not use the DEX SPA as the content host.
  • strategy/static-content-architecture.md is not present yet (marketing#3 AC12). That file belongs in the marketing repo, not this one.

Sibling product hosts (do not become this app)

  • code/cl8y-dex-terraclassic (dex.cl8y.com) — transaction SPA. docker/frontend/nginx.conf uses try_files $uri $uri/ /index.html for /. Exact-location 404 exists only for /og-image.png. frontend-dapp/public/ has no robots.txt / sitemap.xml. That is marketing#2 (DEX repo), not this host. Swap first-party query keys: frontend-dapp/src/utils/swapQueryParams.ts (from, to, optional exactAmount / exactField; unknown keys dropped on rewrite).
  • code/CL8Y-web (cl8y.com) — Vite + React marketing/blog with vite-plugin-prerender, src/blog/SEO.tsx, src/lib/siteOrigin.ts, VITE_SITE_ORIGIN. Product CTAs: src/content/invariants.ts (https://dex.cl8y.com). Wallet stack (wagmi/viem) and three.js live here — not a template to copy. CL8Y-web#6 is cl8y.com deep-links, not this hostname.

Duplicates / already implemented

Work Where Action
Product spec marketing#3 (open) Parent; do not re-file
Repo create marketing#6 (closed) Done; this repo exists
DEX robots/sitemap SPA bug marketing#2 / DEX tree Different repo
This scaffold implement card this issue, prior body Premature; replaced by this architecture → #2 → #3 sequence
App in this tree none Not implemented

Do not add docs.cl8y.com routes to CL8Y-web or the DEX SPA. Do not grow an app folder in cl8y-marketing.

Why the new implementation is needed

Phase 1 exit needs unique canonicals and honest crawl files. The DEX SPA cannot provide that without pretending every client route is a document. cl8y.com is homepage + blog, not methodology / markets / contracts / guides.

This repo was created empty so implement agents would scaffold immediately ([ops-worker] implement card). Architecture first is required: the nginx vs SPA-fallback choice, prerender vs client-only titles, env contract, and route list are the same class of mistakes marketing#2 is already paying for on dex.cl8y.com. Writing those decisions into this tree lets program review (#2) happen before a Coolify image exists.

v0 copy may be stubs. The platform decisions (stack, crawl rules, headers, CTA builder, env, 404 policy) must be explicit and reviewable.

Constraints and guardrails

  1. This issue does not implement the site. No Dockerfile, no nginx.conf, no Vite app, no public/robots.txt as shipped artifacts except as specified in the architecture doc. Follow-on implement is #3 after #2.
  2. One deployable = one repo. Architecture must keep the app in code/cl8y-docs. Not PlasticDigits/cl8y-marketing. Not a new tree inside CL8Y-web or cl8y-dex-terraclassic.
  3. Static only. Vite build → files on nginx. No Node app server, no SSR runtime, no Postgres, no marketing-core API.
  4. Node 24 builder (same floor as DEX frontend image).
  5. nginx non-root in the runtime stage (unprivileged user / unprivileged port). Do not require root in the container. DEX docker/frontend/Dockerfile currently runs nginx as root — do not copy that.
  6. No SPA fallback for /robots.txt or /sitemap.xml. Exact location = + try_files $uri =404. Missing file → 404, not index.html. Unknown HTML paths prefer 404, not a wrong home canonical.
  7. MIME: robots text/plain; sitemap application/xml (set default_type on that location; do not rely on nginx .xml → text/xml alone).
  8. Public build-time env only. VITE_SITE_ORIGIN=https://docs.cl8y.com, VITE_DEX_ORIGIN=https://dex.cl8y.com, optional VITE_INDEXER_ORIGIN=https://indexer.dex.cl8y.com (HTTPS). No WalletConnect ids, LCD keys, forge tokens. Build must fail if required origins are missing or not https://.
  9. Crawler HTML is unique. Do not ship one index.html title/canonical for every path (DEX OG-5). Prerender or equivalent SSG.
  10. CTAs. DEX hrefs are https://dex.cl8y.com (optional /?from=&to= first-party ids, never tickers as execute ids). Campaign keys via URLSearchParams only. Never concatenate visitor query, javascript:, data:, protocol-relative, or ?url= redirectors. Do not read window.location to build product origins.
  11. Claims. v0: no unverified fee, TVL, volume, ranking, or “best DEX” statements. No CoinGecko/CMC marketing.
  12. Do not publish unpublished content/guides/ from the marketing repo. Stubs with honest placeholder methodology are OK in #3.
  13. Out of product scope: wallet connect, trading UI, wrap/mint, pair create, paid media, listing submissions.
  14. Do not noindex docs.cl8y.com from the DEX robots file. DEX Sitemap: pointer at this host is gated on a live docs sitemap (marketing#2 item 8) — not this issue, not #3 until production curl succeeds.
  15. Clickjacking: X-Frame-Options: DENY and frame-ancestors 'none'. X-Content-Type-Options: nosniff on crawl files and HTML.
  16. Canonical host is https://docs.cl8y.com (no trailing slash on origin). www or other aliases 301 to that origin or are not published.
  17. Do not pick machine SKU, snapshot, or a new hypervisor. Rollback = previous image tag on the existing Coolify platform.
  18. Do not apply ready / agent:implement to this issue. Labels: research + agent:research only.

Relevant files

Area Path Role
This repo today README.md Only existing file; keep aligned with architecture
This repo to add docs/ARCHITECTURE.md Decisions: stack, routes, nginx, env, headers, CTA, 404, CI
This repo to add AGENTS.md Short invariants for later implementers (#3)
Strategy cl8y-marketing/strategy/technical-architecture.md Repo boundaries; static+nginx
Strategy cl8y-marketing/strategy/operating-system.md, strategy/90-day-plan.md Crawlable layer ≠ SPA
Parent spec marketing#3 Full v0 AC; #3 implements after review
DEX anti-pattern cl8y-dex-terraclassic/docker/frontend/nginx.conf SPA try_files must not apply to robots/sitemap
DEX image cl8y-dex-terraclassic/docker/frontend/Dockerfile Node 24 builder; add non-root (DEX is root today)
Swap query cl8y-dex-terraclassic/frontend-dapp/src/utils/swapQueryParams.ts from/to ids; unknown keys dropped
Prerender pattern CL8Y-web/vite.config.ts, src/blog/SEO.tsx, src/lib/siteOrigin.ts Unique title/canonical without wagmi
Marketing invariants CL8Y-web/src/content/invariants.ts DEX origin; banned copy
Token addresses CL8Y-web/src/data/tokenDirectory.ts First-party addresses only if /contracts documents them
CI pattern cl8y-pm/.woodpecker.yaml gitleaks + tests; adapt for static Node 24 (no Postgres)
  1. Add docs/ARCHITECTURE.md that records, as decisions (not code):
    • Host https://docs.cl8y.com; Coolify static image; one repo.
    • Stack: Node 24, Vite, React, TypeScript (strict). Dependency set: no wagmi, WalletConnect, three.js, or DEX trading UI.
    • Prerender route list at build (vite-plugin-prerender or lighter SSG). Every shipped path writes HTML whose <title> and rel=canonical match that path. VITE_SITE_ORIGIN baked into absolute canonicals.
    • public/robots.txt / public/sitemap.xml rules (allow /; sitemap loc allowlist https://docs.cl8y.com only).
    • nginx: unprivileged; exact locations for robots/sitemap; hashed assets 404 when missing; unknown paths 404; security headers.
    • Dockerfile shape: node:24-*-slim builder → npm ci + build with public ARG/ENV only → copy dist into nginx html. Healthcheck GET / without root.
    • Route list (stubs in #3): /, /methodology + one child, /markets, /contracts, /guides + one child.
    • CTA helper: dexHref({ from, to, campaign }) → URL + URLSearchParams. Default pair may match CL8Y-web LUNC→CL8Y (uluna + checksummed Terra Classic CL8Y CW20 already in token directory / DEX VITE_CL8Y_TOKEN_ADDRESS). Campaign: utm_source=docs&utm_medium=web&utm_campaign=<page-or-stub-id>.
    • CI: Woodpecker with gitleaks, typecheck, unit tests; Playwright 5 workers called out as #3.
    • Explicit non-goals (wallet, SSR, secrets, DEX sitemap pointer, unapproved guide manuscripts).
  2. Add AGENTS.md pointing at docs/ARCHITECTURE.md and the gate: #2 then #3.
  3. Keep README.md honest (empty app until #3). Do not invent fee/TVL numbers in the architecture examples.
  4. Do not land strategy/static-content-architecture.md here (marketing repo). Do not land DEX robots.txt Sitemap: here.
  5. After merge, #2 reviews this document. #3 is the only v0 implement issue (scaffold + crawl + prerender + stubs + CTA + CI bundled).

Acceptance criteria

  • AC1. docs/ARCHITECTURE.md exists on a PR/main and covers stack, routes, nginx crawl rules, env contract, headers, CTA builder, 404 policy, CI, and non-goals.
  • AC2. AGENTS.md states this repo is the docs.cl8y.com static host, forbids wallet/DEX SPA copy, and names the #1 → #2 → #3 gate.
  • AC3. Document matches marketing#3 on: one-repo-one-deployable, static-only, Node 24, non-root nginx, no SPA fallback for robots/sitemap, unique prerendered titles, public VITE_* only, no unverified claims.
  • AC4. No application scaffold (Dockerfile, Vite app, nginx conf, public/robots.txt as shipped site) in the architecture PR. Those are #3.
  • AC5. No secrets, WalletConnect ids, or unverified performance claims in the architecture text.
  • AC6. Duplicate check recorded: marketing#3 parent, marketing#6 done, marketing#2 is DEX, this tree was README-only.
  • AC7. This issue is not labeled ready or agent:implement.

Test plan (functional paths)

# Path Expect
T1 Read docs/ARCHITECTURE.md Required sections present; decisions are testable by #3
T2 Cross-check marketing#3 constraints 1–16 No silent contradiction
T3 Route list /, methodology (+child), /markets, /contracts, /guides (+child) named
T4 nginx section Exact robots/sitemap locations; missing → 404; unknown HTML → 404
T5 Env section HTTPS public origins only; build-fail if missing
T6 CTA section URLSearchParams; compile-time DEX origin; no window.location product origin
T7 PR diff No package.json / Dockerfile / nginx / page TSX
T8 AGENTS.md Gate #2 then #3; no “implement now”
T9 README Still does not claim the site is live

Test plan (attack, hack, and abuse)

# Vector Expect
A1 Architecture copies DEX try_files SPA fallback for / including robots Rejected in the doc
A2 Architecture allows secrets or VITE_WC_* Forbidden
A3 Architecture puts the app in marketing.git or CL8Y-web Forbidden
A4 CTA from visitor query / open redirect Forbidden; compile-time origin
A5 Sitemap loc to foreign hosts Allowlist docs origin only
A6 Clickjacking omitted Headers required
A7 Container as root Non-root required
A8 Publishing unapproved marketing content/guides/ Out of scope
A9 DEX Sitemap: to docs before live sitemap Gated; not this issue
A10 Applying ready so CAC scaffolds before #2 Labels stay research-only
A11 Indexer http:// or internal hostname Build reject in later #3; called out here
A12 Architecture that 200s unknown paths with home canonical Forbidden (duplicate-title / cloaking)

Verification criteria

  • Human (or #2) can review docs/ARCHITECTURE.md without reading a Vite tree.
  • git ls-files on the architecture PR is docs + README (+ .gitignore if needed) only.
  • marketing#3 is cited; marketing#2 remains the DEX crawl ticket.
  • #3 body still says gated on #2; no ready on #3.
  • No credentials in the document.

Out of scope

  • Scaffolding the Vite/React app, Dockerfile, nginx, CI image, or Coolify hostname attach (that is #3 after #2).
  • Implementing marketing#2 DEX robots/sitemap.
  • Writing strategy/static-content-architecture.md in the marketing repo.
  • Wallet, swap, pool, mint, wrap, Create Token UI.
  • Postgres / marketing-core.
  • Unapproved guide manuscripts, CoinGecko/CMC, paid media.
  • Choosing hypervisor image, SKU, or a new host.

First-pass model recommendation

Recommendation: grok-high. New-repo architecture, nginx/crawl security decisions, and cross-repo boundaries (DEX anti-pattern, CL8Y-web prerender without wagmi). Not a three-file local edit.

Gate

Do not start #2 until AC1–AC4 are on a reviewable PR or main. Do not start #3 until #2 records RECOMMEND: ACCEPT (or FIX items are landed and re-reviewed).

## Summary Write the **architecture** for `code/cl8y-docs` — the first-party static host for **https://docs.cl8y.com**. This issue is **design only**. Do not scaffold Vite, Docker, nginx, or page copy here. Parent / source intent: [PlasticDigits/cl8y-marketing#3](https://git.cl8y.com/PlasticDigits/cl8y-marketing/issues/3). Repo create is done ([marketing#6](https://git.cl8y.com/PlasticDigits/cl8y-marketing/issues/6), closed). Governance stays in `PlasticDigits/cl8y-marketing`. Sequence for this empty repo (do not skip): 1. **This issue** — architecture document on `main`. 2. **[#2](https://git.cl8y.com/code/cl8y-docs/issues/2)** — program review of that document (gated on #1). 3. **[#3](https://git.cl8y.com/code/cl8y-docs/issues/3)** — one bundled v0 implement (gated on #2 ACCEPT). Do not apply `ready` / `agent:implement` to #3 until then. ## Current codebase This repository is README-only (`main` @ initial commit). There is **no** `package.json`, Vite config, `Dockerfile`, `nginx.conf`, `public/robots.txt`, `public/sitemap.xml`, Woodpecker pipeline, prerender, or route modules. `docs.cl8y.com` is not a shipped first-party static site (TLS is not a valid product cert). ### This repo (`code/cl8y-docs`) - `README.md` — one paragraph: TypeScript static site for docs.cl8y.com (Vite + React + TS → nginx), Coolify-deployable; governance in `PlasticDigits/cl8y-marketing`. - No application source. Nothing from marketing#3 is implemented here. ### Governance (`PlasticDigits/cl8y-marketing`) - [marketing#3](https://git.cl8y.com/PlasticDigits/cl8y-marketing/issues/3) is the product spec: one deployable = one repo, Node 24 builder, nginx non-root, honest crawl files, unique prerendered titles/canonicals, stub indexable routes, DEX CTAs via `URLSearchParams`, no unverified claims. - `strategy/technical-architecture.md` — TypeScript + React static sites, Vite → nginx, one repo per deployable, public `VITE_*` only. - `strategy/operating-system.md` — crawlable layer **separate from** the transaction SPA (`robots.txt` / `sitemap.xml` with correct types; prerendered unique titles). - `strategy/90-day-plan.md` Phase 1 — unique canonicals on indexable routes; do not use the DEX SPA as the content host. - `strategy/static-content-architecture.md` is **not** present yet (marketing#3 AC12). That file belongs in the **marketing** repo, not this one. ### Sibling product hosts (do not become this app) - **`code/cl8y-dex-terraclassic` (`dex.cl8y.com`)** — transaction SPA. `docker/frontend/nginx.conf` uses `try_files $uri $uri/ /index.html` for `/`. Exact-location 404 exists only for `/og-image.png`. `frontend-dapp/public/` has no `robots.txt` / `sitemap.xml`. That is [marketing#2](https://git.cl8y.com/PlasticDigits/cl8y-marketing/issues/2) (DEX repo), **not** this host. Swap first-party query keys: `frontend-dapp/src/utils/swapQueryParams.ts` (`from`, `to`, optional `exactAmount` / `exactField`; unknown keys dropped on rewrite). - **`code/CL8Y-web` (`cl8y.com`)** — Vite + React marketing/blog with `vite-plugin-prerender`, `src/blog/SEO.tsx`, `src/lib/siteOrigin.ts`, `VITE_SITE_ORIGIN`. Product CTAs: `src/content/invariants.ts` (`https://dex.cl8y.com`). Wallet stack (wagmi/viem) and three.js live here — **not** a template to copy. `CL8Y-web#6` is cl8y.com deep-links, not this hostname. ### Duplicates / already implemented | Work | Where | Action | |------|--------|--------| | Product spec | marketing#3 (open) | Parent; do not re-file | | Repo create | marketing#6 (closed) | Done; this repo exists | | DEX robots/sitemap SPA bug | marketing#2 / DEX tree | Different repo | | This scaffold implement card | **this issue, prior body** | Premature; replaced by this architecture → #2 → #3 sequence | | App in this tree | none | Not implemented | Do not add `docs.cl8y.com` routes to `CL8Y-web` or the DEX SPA. Do not grow an app folder in `cl8y-marketing`. ## Why the new implementation is needed Phase 1 exit needs unique canonicals and honest crawl files. The DEX SPA cannot provide that without pretending every client route is a document. `cl8y.com` is homepage + blog, not methodology / markets / contracts / guides. This repo was created empty so implement agents would scaffold immediately ([ops-worker] implement card). **Architecture first** is required: the nginx vs SPA-fallback choice, prerender vs client-only titles, env contract, and route list are the same class of mistakes marketing#2 is already paying for on dex.cl8y.com. Writing those decisions into this tree lets program review (#2) happen **before** a Coolify image exists. v0 copy may be stubs. The **platform decisions** (stack, crawl rules, headers, CTA builder, env, 404 policy) must be explicit and reviewable. ## Constraints and guardrails 1. **This issue does not implement the site.** No `Dockerfile`, no `nginx.conf`, no Vite app, no `public/robots.txt` as shipped artifacts except as **specified** in the architecture doc. Follow-on implement is #3 after #2. 2. **One deployable = one repo.** Architecture must keep the app in `code/cl8y-docs`. Not `PlasticDigits/cl8y-marketing`. Not a new tree inside `CL8Y-web` or `cl8y-dex-terraclassic`. 3. **Static only.** Vite build → files on nginx. No Node app server, no SSR runtime, no Postgres, no marketing-core API. 4. **Node 24** builder (same floor as DEX frontend image). 5. **nginx non-root** in the runtime stage (unprivileged user / unprivileged port). Do not require root in the container. DEX `docker/frontend/Dockerfile` currently runs nginx as root — **do not copy that**. 6. **No SPA fallback** for `/robots.txt` or `/sitemap.xml`. Exact `location =` + `try_files $uri =404`. Missing file → **404**, not `index.html`. Unknown HTML paths prefer **404**, not a wrong home canonical. 7. **MIME:** robots `text/plain`; sitemap `application/xml` (set `default_type` on that location; do not rely on nginx `.xml` → `text/xml` alone). 8. **Public build-time env only.** `VITE_SITE_ORIGIN=https://docs.cl8y.com`, `VITE_DEX_ORIGIN=https://dex.cl8y.com`, optional `VITE_INDEXER_ORIGIN=https://indexer.dex.cl8y.com` (HTTPS). No WalletConnect ids, LCD keys, forge tokens. Build must fail if required origins are missing or not `https://`. 9. **Crawler HTML is unique.** Do not ship one `index.html` title/canonical for every path (DEX OG-5). Prerender or equivalent SSG. 10. **CTAs.** DEX hrefs are `https://dex.cl8y.com` (optional `/?from=&to=` first-party ids, never tickers as execute ids). Campaign keys via `URLSearchParams` only. Never concatenate visitor query, `javascript:`, `data:`, protocol-relative, or `?url=` redirectors. Do not read `window.location` to build product origins. 11. **Claims.** v0: no unverified fee, TVL, volume, ranking, or “best DEX” statements. No CoinGecko/CMC marketing. 12. **Do not publish** unpublished `content/guides/` from the marketing repo. Stubs with honest placeholder methodology are OK in #3. 13. **Out of product scope:** wallet connect, trading UI, wrap/mint, pair create, paid media, listing submissions. 14. **Do not** noindex `docs.cl8y.com` from the DEX robots file. DEX `Sitemap:` pointer at this host is **gated** on a live docs sitemap ([marketing#2](https://git.cl8y.com/PlasticDigits/cl8y-marketing/issues/2) item 8) — not this issue, not #3 until production curl succeeds. 15. Clickjacking: `X-Frame-Options: DENY` and `frame-ancestors 'none'`. `X-Content-Type-Options: nosniff` on crawl files and HTML. 16. Canonical host is `https://docs.cl8y.com` (no trailing slash on origin). www or other aliases 301 to that origin or are not published. 17. **Do not pick** machine SKU, snapshot, or a new hypervisor. Rollback = previous image tag on the existing Coolify platform. 18. **Do not** apply `ready` / `agent:implement` to this issue. Labels: `research` + `agent:research` only. ## Relevant files | Area | Path | Role | |------|------|------| | This repo today | `README.md` | Only existing file; keep aligned with architecture | | This repo to add | `docs/ARCHITECTURE.md` | Decisions: stack, routes, nginx, env, headers, CTA, 404, CI | | This repo to add | `AGENTS.md` | Short invariants for later implementers (#3) | | Strategy | `cl8y-marketing/strategy/technical-architecture.md` | Repo boundaries; static+nginx | | Strategy | `cl8y-marketing/strategy/operating-system.md`, `strategy/90-day-plan.md` | Crawlable layer ≠ SPA | | Parent spec | [marketing#3](https://git.cl8y.com/PlasticDigits/cl8y-marketing/issues/3) | Full v0 AC; #3 implements after review | | DEX anti-pattern | `cl8y-dex-terraclassic/docker/frontend/nginx.conf` | SPA `try_files` must not apply to robots/sitemap | | DEX image | `cl8y-dex-terraclassic/docker/frontend/Dockerfile` | Node 24 builder; **add** non-root (DEX is root today) | | Swap query | `cl8y-dex-terraclassic/frontend-dapp/src/utils/swapQueryParams.ts` | `from`/`to` ids; unknown keys dropped | | Prerender pattern | `CL8Y-web/vite.config.ts`, `src/blog/SEO.tsx`, `src/lib/siteOrigin.ts` | Unique title/canonical **without** wagmi | | Marketing invariants | `CL8Y-web/src/content/invariants.ts` | DEX origin; banned copy | | Token addresses | `CL8Y-web/src/data/tokenDirectory.ts` | First-party addresses only if `/contracts` documents them | | CI pattern | `cl8y-pm/.woodpecker.yaml` | gitleaks + tests; adapt for static Node 24 (no Postgres) | ## Recommended direction 1. Add `docs/ARCHITECTURE.md` that records, as decisions (not code): - Host `https://docs.cl8y.com`; Coolify static image; one repo. - Stack: Node 24, Vite, React, TypeScript (strict). Dependency set: no wagmi, WalletConnect, three.js, or DEX trading UI. - Prerender route list at build (`vite-plugin-prerender` or lighter SSG). Every shipped path writes HTML whose `<title>` and `rel=canonical` match that path. `VITE_SITE_ORIGIN` baked into absolute canonicals. - `public/robots.txt` / `public/sitemap.xml` rules (allow `/`; sitemap loc allowlist `https://docs.cl8y.com` only). - nginx: unprivileged; exact locations for robots/sitemap; hashed assets 404 when missing; unknown paths 404; security headers. - Dockerfile shape: `node:24-*-slim` builder → `npm ci` + build with public `ARG`/`ENV` only → copy `dist` into nginx html. Healthcheck `GET /` without root. - Route list (stubs in #3): `/`, `/methodology` + one child, `/markets`, `/contracts`, `/guides` + one child. - CTA helper: `dexHref({ from, to, campaign })` → `URL` + `URLSearchParams`. Default pair may match CL8Y-web LUNC→CL8Y (`uluna` + checksummed Terra Classic CL8Y CW20 already in token directory / DEX `VITE_CL8Y_TOKEN_ADDRESS`). Campaign: `utm_source=docs&utm_medium=web&utm_campaign=<page-or-stub-id>`. - CI: Woodpecker with gitleaks, typecheck, unit tests; Playwright 5 workers called out as #3. - Explicit **non-goals** (wallet, SSR, secrets, DEX sitemap pointer, unapproved guide manuscripts). 2. Add `AGENTS.md` pointing at `docs/ARCHITECTURE.md` and the gate: #2 then #3. 3. Keep `README.md` honest (empty app until #3). Do not invent fee/TVL numbers in the architecture examples. 4. Do **not** land `strategy/static-content-architecture.md` here (marketing repo). Do **not** land DEX `robots.txt` `Sitemap:` here. 5. After merge, #2 reviews this document. #3 is the only v0 implement issue (scaffold + crawl + prerender + stubs + CTA + CI bundled). ## Acceptance criteria - [ ] **AC1.** `docs/ARCHITECTURE.md` exists on a PR/`main` and covers stack, routes, nginx crawl rules, env contract, headers, CTA builder, 404 policy, CI, and non-goals. - [ ] **AC2.** `AGENTS.md` states this repo is the docs.cl8y.com static host, forbids wallet/DEX SPA copy, and names the #1 → #2 → #3 gate. - [ ] **AC3.** Document matches marketing#3 on: one-repo-one-deployable, static-only, Node 24, non-root nginx, no SPA fallback for robots/sitemap, unique prerendered titles, public `VITE_*` only, no unverified claims. - [ ] **AC4.** No application scaffold (`Dockerfile`, Vite app, nginx conf, `public/robots.txt` as shipped site) in the architecture PR. Those are #3. - [ ] **AC5.** No secrets, WalletConnect ids, or unverified performance claims in the architecture text. - [ ] **AC6.** Duplicate check recorded: marketing#3 parent, marketing#6 done, marketing#2 is DEX, this tree was README-only. - [ ] **AC7.** This issue is **not** labeled `ready` or `agent:implement`. ## Test plan (functional paths) | # | Path | Expect | |---|------|--------| | T1 | Read `docs/ARCHITECTURE.md` | Required sections present; decisions are testable by #3 | | T2 | Cross-check marketing#3 constraints 1–16 | No silent contradiction | | T3 | Route list | `/`, methodology (+child), `/markets`, `/contracts`, `/guides` (+child) named | | T4 | nginx section | Exact robots/sitemap locations; missing → 404; unknown HTML → 404 | | T5 | Env section | HTTPS public origins only; build-fail if missing | | T6 | CTA section | `URLSearchParams`; compile-time DEX origin; no `window.location` product origin | | T7 | PR diff | No `package.json` / `Dockerfile` / nginx / page TSX | | T8 | `AGENTS.md` | Gate #2 then #3; no “implement now” | | T9 | README | Still does not claim the site is live | ## Test plan (attack, hack, and abuse) | # | Vector | Expect | |---|--------|--------| | A1 | Architecture copies DEX `try_files` SPA fallback for `/` including robots | Rejected in the doc | | A2 | Architecture allows secrets or `VITE_WC_*` | Forbidden | | A3 | Architecture puts the app in marketing.git or CL8Y-web | Forbidden | | A4 | CTA from visitor query / open redirect | Forbidden; compile-time origin | | A5 | Sitemap loc to foreign hosts | Allowlist docs origin only | | A6 | Clickjacking omitted | Headers required | | A7 | Container as root | Non-root required | | A8 | Publishing unapproved marketing `content/guides/` | Out of scope | | A9 | DEX `Sitemap:` to docs before live sitemap | Gated; not this issue | | A10 | Applying `ready` so CAC scaffolds before #2 | Labels stay research-only | | A11 | Indexer `http://` or internal hostname | Build reject in later #3; called out here | | A12 | Architecture that 200s unknown paths with home canonical | Forbidden (duplicate-title / cloaking) | ## Verification criteria - Human (or #2) can review `docs/ARCHITECTURE.md` without reading a Vite tree. - `git ls-files` on the architecture PR is docs + README (+ `.gitignore` if needed) only. - marketing#3 is cited; marketing#2 remains the DEX crawl ticket. - #3 body still says gated on #2; no `ready` on #3. - No credentials in the document. ## Out of scope - Scaffolding the Vite/React app, Dockerfile, nginx, CI image, or Coolify hostname attach (that is #3 after #2). - Implementing [marketing#2](https://git.cl8y.com/PlasticDigits/cl8y-marketing/issues/2) DEX robots/sitemap. - Writing `strategy/static-content-architecture.md` in the marketing repo. - Wallet, swap, pool, mint, wrap, Create Token UI. - Postgres / marketing-core. - Unapproved guide manuscripts, CoinGecko/CMC, paid media. - Choosing hypervisor image, SKU, or a new host. ## First-pass model recommendation Recommendation: **grok-high**. New-repo architecture, nginx/crawl security decisions, and cross-repo boundaries (DEX anti-pattern, CL8Y-web prerender without wagmi). Not a three-file local edit. ## Gate Do not start [#2](https://git.cl8y.com/code/cl8y-docs/issues/2) until AC1–AC4 are on a reviewable PR or `main`. Do not start [#3](https://git.cl8y.com/code/cl8y-docs/issues/3) until #2 records **RECOMMEND: ACCEPT** (or FIX items are landed and re-reviewed).

cl8y-agent-control: queued implement job 47a20fe6-63ec-4dd0-8ad4-07ec54625829 (not executed; no Hetzner VM).

cl8y-agent-control: queued `implement` job `47a20fe6-63ec-4dd0-8ad4-07ec54625829` (not executed; no Hetzner VM).
PlasticDigits changed title from feat(docs): Coolify TS static scaffold for docs.cl8y.com to design(docs): architecture for docs.cl8y.com static host 2026-09-13 05:37:06 +00:00

Issue repair: this was an agent:implement + ready scaffold card on an empty repo. That skipped architecture and program review.

Now:

  1. This issue — architecture only (docs/ARCHITECTURE.md + AGENTS.md). Labels research + agent:research. ready / agent:implement removed.
  2. #2 — program review of that document. Gated on #1. Same research labels. Verdict RECOMMEND: ACCEPT|FIX|BLOCK.
  3. #3 — one bundled v0 implement (Vite + nginx non-root + crawl files + prerender stubs + DEX CTAs + CI). Label feature only. Do not apply ready until #2 ACCEPT.

Duplicates checked: marketing#3 is the parent spec; marketing#6 (repo create) is closed; marketing#2 is DEX crawl files; nothing in this tree is implemented yet. Related features are bundled into #3, not split into per-route tickets.

Issue repair: this was an `agent:implement` + `ready` scaffold card on an empty repo. That skipped architecture and program review. Now: 1. **This issue** — architecture only (`docs/ARCHITECTURE.md` + `AGENTS.md`). Labels `research` + `agent:research`. `ready` / `agent:implement` removed. 2. **#2** — program review of that document. Gated on #1. Same research labels. Verdict `RECOMMEND: ACCEPT|FIX|BLOCK`. 3. **#3** — one bundled v0 implement (Vite + nginx non-root + crawl files + prerender stubs + DEX CTAs + CI). Label `feature` only. Do not apply `ready` until #2 ACCEPT. Duplicates checked: marketing#3 is the parent spec; marketing#6 (repo create) is closed; marketing#2 is DEX crawl files; nothing in this tree is implemented yet. Related features are bundled into #3, not split into per-route tickets.

Architecture for #1 is on reviewable PR #4 (issue/1-docs-architecture). Design only — no Vite/Docker/nginx/crawl scaffold.

Duplicates (unchanged): marketing#3 parent spec; marketing#6 repo create (done); marketing#2 DEX crawl (other repo); this tree was README-only.

Decisions recorded in docs/ARCHITECTURE.md:

  • Host https://docs.cl8y.com; Node 24 → Vite/React/TS static files → nginxinc/nginx-unprivileged:1.27-alpine on 8080 (DEX frontend image is root on 80 — not copied).
  • Closed prerender allowlist: /, /methodology + /methodology/sources, /markets, /contracts, /guides + /guides/open-the-dex. Unknown paths 404 (no SPA /index.html fallback).
  • Crawl: exact location = + try_files $uri =404; robots text/plain; sitemap application/xml via default_type.
  • CTA dexHref: from=uluna, to= Terra Classic CL8Y CW20 terra16wtml2q66g82fdkx66tap0qjkahqwp4lwq3ngtygacg5q0kzycgqvhpax3. DEX rewrite drops utm_* (first-touch).
  • Env: required VITE_SITE_ORIGIN / VITE_DEX_ORIGIN HTTPS; optional indexer origin if set must equal https://indexer.dex.cl8y.com. No VITE_WC_*.
  • CI (for #3): gitleaks + Node 24; no Postgres (cl8y-pm pipeline includes Postgres — do not copy that service).
  • Out of this repo: marketing AC12 strategy/static-content-architecture.md; marketing AC11 DEX Sitemap: pointer (gated on live docs sitemap).

Gate: #2 reviews this PR/document (RECOMMEND: ACCEPT|FIX|BLOCK). Do not apply ready / agent:implement to #3 until ACCEPT.

Evidence: sibling nginx/Dockerfile/swap params in cl8y-dex-terraclassic; prerender/SEO/origins/token directory in CL8Y-web; Woodpecker+Postgres in cl8y-pm/.woodpecker.yaml; unprivileged image listens 8080 (nginxinc/docker-nginx-unprivileged).

Architecture for #1 is on reviewable PR [#4](https://git.cl8y.com/code/cl8y-docs/pulls/4) (`issue/1-docs-architecture`). Design only — no Vite/Docker/nginx/crawl scaffold. **Duplicates (unchanged):** marketing#3 parent spec; marketing#6 repo create (done); marketing#2 DEX crawl (other repo); this tree was README-only. **Decisions recorded in `docs/ARCHITECTURE.md`:** - Host `https://docs.cl8y.com`; Node 24 → Vite/React/TS static files → `nginxinc/nginx-unprivileged:1.27-alpine` on **8080** (DEX frontend image is root on 80 — not copied). - Closed prerender allowlist: `/`, `/methodology` + `/methodology/sources`, `/markets`, `/contracts`, `/guides` + `/guides/open-the-dex`. Unknown paths **404** (no SPA `/index.html` fallback). - Crawl: exact `location =` + `try_files $uri =404`; robots `text/plain`; sitemap `application/xml` via `default_type`. - CTA `dexHref`: `from=uluna`, `to=` Terra Classic CL8Y CW20 `terra16wtml2q66g82fdkx66tap0qjkahqwp4lwq3ngtygacg5q0kzycgqvhpax3`. DEX rewrite drops `utm_*` (first-touch). - Env: required `VITE_SITE_ORIGIN` / `VITE_DEX_ORIGIN` HTTPS; optional indexer origin if set must equal `https://indexer.dex.cl8y.com`. No `VITE_WC_*`. - CI (for #3): gitleaks + Node 24; **no** Postgres (cl8y-pm pipeline includes Postgres — do not copy that service). - Out of this repo: marketing AC12 `strategy/static-content-architecture.md`; marketing AC11 DEX `Sitemap:` pointer (gated on live docs sitemap). **Gate:** #2 reviews this PR/document (`RECOMMEND: ACCEPT|FIX|BLOCK`). Do not apply `ready` / `agent:implement` to #3 until ACCEPT. **Evidence:** sibling nginx/Dockerfile/swap params in `cl8y-dex-terraclassic`; prerender/SEO/origins/token directory in `CL8Y-web`; Woodpecker+Postgres in `cl8y-pm/.woodpecker.yaml`; unprivileged image listens 8080 ([nginxinc/docker-nginx-unprivileged](https://github.com/nginxinc/docker-nginx-unprivileged)).

Merge check for #4 (issue/1-docs-architecture @ 8fa5503) vs this issue's AC.

Conflicts: none. main is still the initial commit; README merges cleanly.

Woodpecker: code/cl8y-docs is enabled on ci.cl8y.com (repo id 19) but has zero pipelines. Forgejo commit status is empty (fj pr status cannot parse an empty CI variant). This is expected: AC4 / docs/ARCHITECTURE.md §13 and §17 forbid .woodpecker.yaml on the architecture PR. Not a red check. Pipeline lands in #3 after #2 ACCEPT.

AC (this issue):

  • AC1 pass — docs/ARCHITECTURE.md covers stack (§3), routes (§4), nginx crawl/404 (§6–7), env (§9), headers (§11), CTA (§10), CI (§13), non-goals (§14).
  • AC2 pass — AGENTS.md names docs.cl8y.com, forbids wallet/DEX SPA copy, and states #1 → #2 → #3 (do not start #3 until ACCEPT).
  • AC3 pass — §15 maps marketing#3 AC1–AC12; Node 24, non-root nginxinc/nginx-unprivileged on 8080, no SPA fallback, unique prerender, public VITE_* only, no unverified claims.
  • AC4 pass — git ls-files is markdown + .gitignore only (no package.json / Dockerfile / nginx.conf / crawl files / Vite / Woodpecker).
  • AC5 pass — WalletConnect / VITE_WC_* only as forbidden; no secrets; fee/TVL/volume only as bans.
  • AC6 pass — §1 duplicate table (marketing#3 parent, #6 done, #2 DEX, this tree README-only).
  • AC7 pass — labels remain research + agent:research (not ready / agent:implement).

T1–T9 / A1–A12: covered in ARCHITECTURE §§4–18. Explicit reject table for A1–A12 is §18.

Gate: merging this closes architecture AC. #2 stays open for RECOMMEND: ACCEPT|FIX|BLOCK. Do not start #3 and do not apply ready / agent:implement until #2 ACCEPT.

Merge check for [#4](https://git.cl8y.com/code/cl8y-docs/pulls/4) (`issue/1-docs-architecture` @ `8fa5503`) vs this issue's AC. **Conflicts:** none. `main` is still the initial commit; README merges cleanly. **Woodpecker:** `code/cl8y-docs` is enabled on ci.cl8y.com (repo id 19) but has **zero pipelines**. Forgejo commit status is empty (`fj pr status` cannot parse an empty CI variant). This is expected: AC4 / `docs/ARCHITECTURE.md` §13 and §17 forbid `.woodpecker.yaml` on the architecture PR. Not a red check. Pipeline lands in #3 after #2 ACCEPT. **AC (this issue):** - AC1 pass — `docs/ARCHITECTURE.md` covers stack (§3), routes (§4), nginx crawl/404 (§6–7), env (§9), headers (§11), CTA (§10), CI (§13), non-goals (§14). - AC2 pass — `AGENTS.md` names docs.cl8y.com, forbids wallet/DEX SPA copy, and states #1 → #2 → #3 (do not start #3 until ACCEPT). - AC3 pass — §15 maps marketing#3 AC1–AC12; Node 24, non-root `nginxinc/nginx-unprivileged` on 8080, no SPA fallback, unique prerender, public `VITE_*` only, no unverified claims. - AC4 pass — `git ls-files` is markdown + `.gitignore` only (no `package.json` / `Dockerfile` / `nginx.conf` / crawl files / Vite / Woodpecker). - AC5 pass — WalletConnect / `VITE_WC_*` only as forbidden; no secrets; fee/TVL/volume only as bans. - AC6 pass — §1 duplicate table (marketing#3 parent, #6 done, #2 DEX, this tree README-only). - AC7 pass — labels remain `research` + `agent:research` (not `ready` / `agent:implement`). **T1–T9 / A1–A12:** covered in ARCHITECTURE §§4–18. Explicit reject table for A1–A12 is §18. **Gate:** merging this closes architecture AC. [#2](https://git.cl8y.com/code/cl8y-docs/issues/2) stays open for `RECOMMEND: ACCEPT|FIX|BLOCK`. Do not start [#3](https://git.cl8y.com/code/cl8y-docs/issues/3) and do not apply `ready` / `agent:implement` until #2 ACCEPT.

Correction: the #1 architecture (and merge comment on #4) was wrong to treat .woodpecker.yaml as forbidden scaffold. Issue #1 AC4 lists Dockerfile/Vite/nginx/robots, not CI. Missing Woodpecker is NO_CI_MISSING_WOODPECKER (CAC #154).

Fix: #5 / #6. Gitleaks + nonempty-tree now; Node/Playwright still #3.

Correction: the #1 architecture (and merge comment on [#4](https://git.cl8y.com/code/cl8y-docs/pulls/4)) was **wrong** to treat `.woodpecker.yaml` as forbidden scaffold. Issue #1 AC4 lists Dockerfile/Vite/nginx/robots, not CI. Missing Woodpecker is `NO_CI_MISSING_WOODPECKER` (CAC #154). Fix: [#5](https://git.cl8y.com/code/cl8y-docs/issues/5) / [#6](https://git.cl8y.com/code/cl8y-docs/pulls/6). Gitleaks + nonempty-tree now; Node/Playwright still #3.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-docs#1
No description provided.