Files
0pt/CHANGELOG.md
T
Simon AxelssonandClaude Opus 5 8ebc90ffa3
masterplan-lock / check (pull_request) Successful in 4s
masterplan-lock / check (push) Successful in 7s
CI (SIAX Cloud) / security (pull_request) Successful in 17s
CI (SIAX Cloud) / security (push) Successful in 18s
CI (SIAX Cloud) / quality (push) Successful in 39s
CI (SIAX Cloud) / integration (pull_request) Successful in 41s
CI (SIAX Cloud) / integration (push) Successful in 44s
Security Scan (SIAX Cloud) / security (pull_request) Successful in 1m37s
Security Scan (SIAX Cloud) / security (push) Successful in 1m35s
CI (SIAX Cloud) / quality (pull_request) Successful in 3m4s
fix(security,repo-honesty): real security headers + a doctor that checks truth, not presence
Two defects of the same kind: the repo asserted things about itself that were
not true, and nothing in it could tell.

A. CHANGELOG.md claimed CSP/HSTS/X-Frame-Options since 2026-08. `main`
   contained none of it -- a repo-wide grep for
   `content-security-policy|strict-transport-security|x-frame-options` matched
   exactly one file, CHANGELOG.md itself. The only header on 0pt.app was
   Vercel's own HSTS default, which does not survive the move to
   Coolify/Traefik that siax.config.json and .gitea/workflows/cd.yml are
   already pointed at.

   - next.config.mjs: `headers()` on `/(.*)` with CSP (frame-ancestors 'none',
     base-uri 'self', object-src 'none', form-action 'self'), HSTS,
     X-Frame-Options DENY, X-Content-Type-Options, Referrer-Policy,
     Permissions-Policy; plus poweredByHeader: false. Same idiom as
     sax3l/siax-io's next.config.mjs.
   - Carve-outs re-derived against THIS tree, not inherited: the stale
     `ci/security-headers-and-scanning` branch (8d2ff73, 142 commits behind)
     carried a `form-action https://checkout.stripe.com` allowance justified
     by a redirect that is actually `window.location.href`, which form-action
     does not govern -- dropped rather than kept as decorative permission.
     frame-src is derived from NOCODB_BASE_URL/METABASE_SITE_URL, the same env
     the components/embed.tsx iframe is configured from, so wiring that
     component up cannot be silently blanked by this header (the failure
     sax3l/siax-io recorded as SIO-013). 'unsafe-eval' is dev-only.
   - tests/security/security-headers.test.ts: blocking regression gate inside
     `pnpm run test`, i.e. the required "CI (SIAX Cloud) / quality" context.
     Asserts against expectations written out in the test, not imported from
     the config. Proven to fail: removing X-Frame-Options -> `pnpm run test`
     exits 1; weakening frame-ancestors to 'self' -> exits 1.

B. scripts/siax-doctor.mjs validated that siax.repo.json fields were PRESENT,
   never what they said. Measured on this tree: operationalState "running"
   (false) and "down" (true) both gave 40/40, exit 0, byte-identical output.

   - Value checks (enums, git.siax.io const, fleet node pattern, bare-hostname
     form), cross-file consistency (declared prod hostnames must appear in
     this repo's own siax.config.json / contracts/openapi.yaml, and vice
     versa), and GOV-06 substantiation: operationalState must be backed by a
     dated probe record in evidence/operational-state.json that agrees with
     it. Liveness is not knowable offline; whether a liveness CLAIM is backed
     is. Offline, deterministic, no network -- CI can never go red for want of
     an internet connection.
   - `--live` (opt-in) probes the declared hosts for real and checks the
     headers on the wire; `--write-evidence` refreshes the record. Not wired
     into CI on purpose.
   - tests/repo/siax-doctor.test.ts runs the doctor inside `pnpm run test` and
     asserts the negative cases against fixture trees (SIAX_DOCTOR_ROOT), so
     the "cannot fail" defect cannot come back.

C. CHANGELOG.md is now true. The header claim stands; the four claims it was
   bundled with (Trivy --exit-code 1, gitleaks, SHA-pinned actions, SBOM +
   license artifacts) are still false on main and are listed as not
   implemented instead of left asserted. All four live in .gitea/workflows/**,
   a protected path on this repo, so they need an authorised workflow change.

Nothing under .gitea/workflows/** or .gitignore is touched.

Verified: pnpm run typecheck exit 0; pnpm run test exit 0 (74 files / 965
tests passed, up from 72/945); pnpm run build exit 0 and the built
.next/routes-manifest.json carries all six headers; node scripts/siax-doctor.mjs
55/55 exit 0; --live 57/57 exit 0. eslint on the new files exit 0 (repo lint is
advisory and its 17 pre-existing problems are unchanged).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 02:43:32 +02:00

129 lines
7.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
> BACKLOG P1-3. Format loosely follows [Keep a Changelog](https://keepachangelog.com/).
> No git tags/releases exist yet for this repo — this file starts the practice
> retroactively from real `git log` history rather than inventing version
> numbers that were never cut. Dates are commit dates on `main`, not calendar
> "release" dates (there have been none).
## [Unreleased]
- `feat(deploy)`: appen är nu faktiskt byggbar och körbar utanför Vercel.
`next.config.mjs` sätter `output: "standalone"`; Dockerfilen är omskriven
till samma form som `sax3l/siax-io` (som redan körs självhostat på flottan):
fyra steg, `node:22-alpine` (matchar CI:s nodversion, var 24), `libc6-compat`,
icke-root, HEALTHCHECK mot `/api/health`, npm/npx borttaget ur runtime-imagen.
Uppmätt: image 1,33 GB -> 423 MB. `--ignore-workspace` borttaget (dess
motivering var inaktuell sedan P1-21) så containerinstallationen är samma
kommando som CI kör; `--prod=false` tillagt eftersom Coolify sätter
NODE_ENV=production vid byggtid och pnpm då hoppar över devDependencies.
- `fix(images)`: `images.unoptimized: true` borttaget — det var v0-generatorns
standardvärde, inte ett val (`next.config.mjs` var orörd sedan initial commit).
Självhostad bildoptimering kräver att `sharp` faktiskt finns i runtime-imagen:
`next build` spårar den inte in i `.next/standalone`, och
`outputFileTracingIncludes` får Turbopack att panika på pnpms symlänkar. Sharp
levereras därför i ett eget npm-steg i Dockerfile, pinnat till versionen i
`pnpm-lock.yaml`. Verifierat i containern: `/_next/image` svarar 200 med
`image/avif` (1,46 MB PNG -> 6,7 kB).
- fix(security): response security headers are set by the app itself, in
`next.config.mjs` `headers()` — CSP (incl. `frame-ancestors 'none'`,
`base-uri 'self'`, `object-src 'none'`, `form-action 'self'`), HSTS,
X-Frame-Options `DENY`, X-Content-Type-Options, Referrer-Policy,
Permissions-Policy — plus `poweredByHeader: false`. Regression-gated by
`tests/security/security-headers.test.ts` (blocking, runs in
`pnpm run test`) and re-checkable against a live origin with
`node scripts/siax-doctor.mjs --live`. (BACKLOG P0-13, P1-15)
Correcting this file: from 2026-08 until 2026-09-07 this bullet also
claimed the headers themselves, while `main` contained none — a repo-wide
grep for `content-security-policy|strict-transport-security|
x-frame-options` matched only CHANGELOG.md. The only thing setting HSTS on
0pt.app was Vercel's platform default (still observable on the 402
responses recorded in `evidence/operational-state.json`), which is not this
repo's and does not survive the move to Coolify/Traefik. The claim is true
as of this entry; the four claims it was bundled with are not, and are
tracked in the section below.
- fix(repo-honesty): `scripts/siax-doctor.mjs` validated that
`siax.repo.json` fields were PRESENT, never what they said — measured:
`operationalState: "running"` (false at the time; every declared host
answered 402/404) and `"down"` (true) both produced 40/40 and
byte-identical output. It now validates values (enums, hostname form),
cross-file consistency (declared prod hostnames must appear in this repo's
own deploy/contract config, and vice versa), and requires
`operationalState` to be backed by a dated probe record in
`evidence/operational-state.json` that agrees with it. Offline and
deterministic by default; `--live` (opt-in) probes the real hosts.
Gated by `tests/repo/siax-doctor.test.ts`, which also asserts the negative
cases against fixture trees. (BACKLOG P1-19, control AGENT-04, GOV-06)
- fix(health): `/api/health` reports real per-dependency status instead of a
hardcoded `ok: true`; new `/api/ready` readiness endpoint. (BACKLOG P0-3)
- docs/chore: repo-hygiene pass — schema-valid `siax.repo.json`/`siax.config.json`
(standard v2.0), ADRs, this CHANGELOG, `siax:doctor` script, devcontainer,
`docs/architecture/ARCHITECTURE.md` §5 "Ytor och paritet", CODEOWNERS,
stale-link cleanup. (BACKLOG P1-1, P1-2, P1-3, P1-19, P1-20, P1-21, P2-5, P3-5)
### Claimed here before 2026-09-07 but NOT implemented — status 2026-09-15
These were asserted by the earlier `fix(security)` bullet and were not true on
`main` at that time. Current state (measured against `main`, 2026-09-15):
- CI security gates now ENFORCE: `security.yml` and `nightly.yml` run Trivy
with `--exit-code 1` (landed via `ci/make-required-security-gate-real`,
2026-09-08). (BACKLOG P0-16 — CLOSED)
- gitleaks is still not run anywhere in this repo. (BACKLOG P0-19 — OPEN)
- Actions are still NOT pinned to commit SHAs (`actions/checkout@v4` etc.).
(BACKLOG P1-14 — OPEN)
- No SBOM and no license inventory are produced or uploaded as CI artifacts.
(OPEN; the `fix/wire-signed-digest-to-deploy` branch carries a prepared
release chain that needs its cosign/registry secrets before it can land.)
## 2026-08-11 — Evasion-stack decision provenance
- Attributed and dated the account-owner's keep-decision on `services/scrapers/*`
across `SECURITY.md`, `CONTRIBUTING.md`, `prompts/002-compliance-audit.md`,
`renovate.json`, `.gitea/workflows/security.yml`, `docs/slo.md`.
## 2026-08-09/10 — Golden Standard compliance pass (FAS 0–10)
- `docs/compliance/{INVENTORY,RELATIONS,OSS-INVENTORY,GOLDEN-STANDARD-REPORT,BACKLOG,SLUTRAPPORT}.md`
written; `siax.repo.json` classification recorded.
- `fix(security)`: `GET /api/orders` and `GET /api/admin/metrics` now require
auth (were live 200 anonymous — the routes sit outside `middleware.ts`'s
`/api/0pt/:path*` matcher).
- `fix(deploy)`: `siax.config.json` `deploy.target` flipped from `vercel` to
`coolify` (config-only; the live instance is still on Vercel until a
Coolify app is provisioned and DNS is cut over).
- `feat(contract)`: OpenAPI 3.1 SSOT added (`contracts/openapi.yaml`);
`@vercel/analytics` and `next/font/google` removed in favor of self-hosted
`@fontsource-variable/*` (AR-4).
- `ci(workflows)`: CD/security/nightly split under `.gitea/workflows/`.
- README/SECURITY/CONTRIBUTING/AGENTS + runbooks (deploy, incident,
backup-restore, oncall) + SLO doc added.
## 2026-08 — Auth + capability hardening ("Wave 2/3")
- `fix(auth)`: `AUTH_ENFORCE` default flipped to fail-closed on `/api/0pt/*`.
- Zitadel Bearer-token verification wired into `middleware.ts`
(`src/auth/zitadel.ts`, `src/auth/context.ts`).
- P0 capability layer built out: `0pt.query_source`, document
capture/extraction, Iceberg write-boundary, external writeback, dataset
quality flow docs.
- CI quality gate added (typecheck/lint/test), `.env.example` committed
(previously blocked by a blanket `.gitignore` `.env*` rule).
## 2026-07-13 — Scraper stack (kept, see BACKLOG P3-3 / SECURITY.md)
- `services/scrapers/*` (WAF-bypass, proxy rotation, ClickHouse pipeline) was
added on the Gitea line of history. This is the subject of the dated,
attributed account-owner decision referenced throughout this repo's
compliance docs — not reopened or altered by this changelog entry.
## Earlier
- Initial Next.js scaffold, order/catalog/portal UI, FastAPI engine
scaffold, i18n (EN/SV) and accessibility passes, lakehouse write-path
ownership ADR (`ADR-0030`, in the `n0d` repo).
[Unreleased]: https://git.siax.io/sax3l/0pt/compare/main...HEAD