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

7.2 KiB
Raw Blame History

Changelog

BACKLOG P1-3. Format loosely follows Keep a Changelog. 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).