E2E sign+verify: real valid FNMT cert (current-cert.p12) + generated self-signed p12 #39

Closed
opened 2026-10-15 01:19:19 +02:00 by EduarD3V · 51 comments
Owner

Goal

Prove the full sign→verify loop with TWO keystores through the committed paths (UI flow + headless script):

  1. Real valid cert work/certs/current-cert.p12 (FNMT citizen cert, valid until 2028-12-31) — signs cleanly, verify reports valid.
  2. Generated self-signed p12 (existing test fixture generator from #24) — same loop.

Secrets (HARD RULES)

  • Both real keystores live in gitignored work/certs/ (old-cert.p12, current-cert.p12). PIN in gitignored .p12pass (same for both). NEVER print/echo/commit PIN or cert contents.
  • Scripts read at runtime; missing files ⇒ skip with notice + exit 0 (CI stays green).

Tasks

  1. Extend scripts/smoke-real.sh / driver: --cert <path> arg; run for current-cert.p12 AND a generated fixture p12 (temp, never committed).
  2. Assert: signature valid, cert identity matches subject, verify output includes validity dates.
  3. UI path (manual-evidence): document in README how to load each in the app; screenshot-able flow description.

Gates

pnpm test green, build clean, smoke exits 0 with verifiable signatures for both keystores.

Non-goals

No expired-cert logic (issue for expired handling is separate). No MCP, no PWA.

## Goal Prove the full sign→verify loop with TWO keystores through the committed paths (UI flow + headless script): 1. Real valid cert `work/certs/current-cert.p12` (FNMT citizen cert, valid until 2028-12-31) — signs cleanly, verify reports valid. 2. Generated self-signed p12 (existing test fixture generator from #24) — same loop. ## Secrets (HARD RULES) - Both real keystores live in gitignored `work/certs/` (old-cert.p12, current-cert.p12). PIN in gitignored `.p12pass` (same for both). NEVER print/echo/commit PIN or cert contents. - Scripts read at runtime; missing files ⇒ skip with notice + exit 0 (CI stays green). ## Tasks 1. Extend `scripts/smoke-real.sh` / driver: `--cert <path>` arg; run for current-cert.p12 AND a generated fixture p12 (temp, never committed). 2. Assert: signature valid, cert identity matches subject, verify output includes validity dates. 3. UI path (manual-evidence): document in README how to load each in the app; screenshot-able flow description. ## Gates pnpm test green, build clean, smoke exits 0 with verifiable signatures for both keystores. ## Non-goals No expired-cert logic (issue for expired handling is separate). No MCP, no PWA.
Author
Owner

bullet: coder launched on branch issue/39-e2e-sign-verify-real-valid-fnm (model nan/deepseek-v4-flash).

bullet: coder launched on branch `issue/39-e2e-sign-verify-real-valid-fnm` (model `nan/deepseek-v4-flash`).
Author
Owner

Claimed — branch issue/39-e2e-sign-verify-real-valid-fnm. Doing: new scripts/smoke-real.sh headless E2E driver (load p12 → CAdES sign → verify, cross-checked with openssl) run against both the real FNMT keystore and a generated fixture, plus README UI-flow section.

Claimed — branch `issue/39-e2e-sign-verify-real-valid-fnm`. Doing: new `scripts/smoke-real.sh` headless E2E driver (load p12 → CAdES sign → verify, cross-checked with openssl) run against both the real FNMT keystore and a generated fixture, plus README UI-flow section.
Author
Owner

Issue #39 — E2E sign+verify with two keystores

Restated goal

Prove, through the app's own committed code paths, that a full loop load PKCS#12 → pick signer →
CAdES-sign a document → verify the produced .csig
works for two keystores:

  1. the real FNMT citizen keystore work/certs/current-cert.p12 (PIN in gitignored .p12pass), and
  2. a self-signed p12 generated on the fly by the existing src/test/fixtures.ts helper (temp, never committed).

"Done" = bash scripts/smoke-real.sh exits 0 printing a verifiable signature for each keystore
(with the generated one always, with the real one whenever its gitignored files exist — otherwise a
notice and exit 0), pnpm test green, pnpm build clean, and README documenting both the headless
run and the equivalent UI flow (screenshot-able step list).

Scope

Files I will change (all additions except README/package.json):

  • NEW scripts/smoke-real.sh — CLI wrapper: --cert <path>, --pin-file <path>,
    --generated/--no-generated. Missing cert/PIN file ⇒ notice + exit 0.
  • NEW scripts/smoke-real.test.ts — the driver itself: runs the loop for
    SMOKE_CERT (if set) and for the generated fixture; asserts signature validity, signer identity,
    validity dates; cross-checks with openssl.
  • NEW scripts/vitest.smoke.config.ts — minimal vitest config so the driver is runnable
    outside the app suite (root include is src/**/*.test.ts, node environment, no jsdom).
  • MOD package.json — one line: "verify:real": "bash scripts/smoke-real.sh" (mirrors
    the existing verify:zip).
  • MOD README.md — new section: headless run + the UI flow for both keystores.

Explicitly not touched: src/lib/** (no library change — the loop must run on today's code),
test/**, src/test/**, Containerfile, Caddyfile, vite.config.ts, anything under
original/, work/, .p12pass, .gitignore. No new dependency. No PIN or cert bytes are ever
printed, echoed or committed.

Ordered tasks

# Task Verify with
1 Probe the real keystore read-only (absolute paths into the operator checkout, never copied) to see what loadSignerSession + verifyArtifact report, incl. the certChain check bash scripts/smoke-real.sh --cert <abs>/work/certs/current-cert.p12 --pin-file <abs>/.p12pass (temp probe first)
2 Write the driver + smoke vitest config + wrapper; loop for the generated fixture bash scripts/smoke-real.sh --generated ⇒ exit 0, openssl cms -verify OK
3 Wire the real certificate case and both-at-once default bash scripts/smoke-real.sh --cert <abs>/work/certs/current-cert.p12 --pin-file <abs>/.p12pass ⇒ exit 0
4 Skip path (missing keystore) stays green bash scripts/smoke-real.sh --cert /tmp/does-not-exist.p12 ⇒ exit 0 + notice
5 README: headless run + UI flow for each keystore pnpm exec vitest run unaffected; re-read section
6 Gates pnpm test, pnpm build, pnpm check, git diff --check

Commit after each verified task: <area>: <what> (Refs #39).

Open questions → assumption taken

  1. work/certs/ + .p12pass are gitignored and absent from this worktree (they live only in the
    operator's checkout). Assumption: the wrapper defaults to repo-relative work/certs/current-cert.p12
    and .p12pass, skips with a notice when they are missing, and the real-keystore evidence for this
    issue is produced by pointing --cert/--pin-file at those read-only absolute paths (never
    copied into the worktree, never printed). The committed script keeps working for any user who
    drops a keystore in work/certs/.
  2. Chain trust. signCadesDetached embeds only the signer certificate, and the bundled roots
    hold the FNMT root. The self-signed fixture's leaf is signed by the fixture CA, which is not a
    bundled root — so certChain may fail. Assumption: the driver asserts report.ok and supplies
    the container's own non-signer certificates as extra trustedRoots (which is what the existing
    verify tests do with the throwaway CA). If the FNMT chain still cannot be built from the bundled
    roots, the driver asserts every check except certChain and prints the certChain status and
    reason explicitly rather than hiding it.
  3. UI path evidence. No browser automation is requested for #39 ("manual-evidence"), so the UI
    flow is documented as a screenshot-able numbered step list in README, generated from the real
    component names/labels in src/lib/App.svelte + CertPicker.svelte.

Risk and fallback

  • Risk: the real p12 uses legacy PBES1 (3DES/RC2) and is rejected by the parser → the loop
    cannot complete and #39 would be blocked by #5's PBES2-only limit. Fallback: report the exact
    rejection message, keep the fixture case green, and report PARTIAL with the reason.
  • Risk: pnpm check/svelte-check picks up the new scripts/*.ts (tsconfig include covers
    *) and complains about Node globals. Fallback: the driver uses only node: imports and
    process (both typed by the already-installed @types/node).
  • Risk: vitest with a second config changes nothing about pnpm test (root include is
    src/**/*.test.ts), so the smoke file must not be matched by the app suite. Fallback: keep the
    driver under scripts/ (outside src/) and confirm with a run of pnpm test --reporter=basic.
# Issue #39 — E2E sign+verify with two keystores ## Restated goal Prove, through the app's own committed code paths, that a full loop *load PKCS#12 → pick signer → CAdES-sign a document → verify the produced `.csig`* works for **two** keystores: 1. the real FNMT citizen keystore `work/certs/current-cert.p12` (PIN in gitignored `.p12pass`), and 2. a self-signed p12 generated on the fly by the existing `src/test/fixtures.ts` helper (temp, never committed). "Done" = `bash scripts/smoke-real.sh` exits 0 printing a verifiable signature for each keystore (with the generated one always, with the real one whenever its gitignored files exist — otherwise a notice and exit 0), `pnpm test` green, `pnpm build` clean, and README documenting both the headless run and the equivalent UI flow (screenshot-able step list). ## Scope Files I will change (all additions except README/package.json): - **NEW** `scripts/smoke-real.sh` — CLI wrapper: `--cert <path>`, `--pin-file <path>`, `--generated/--no-generated`. Missing cert/PIN file ⇒ notice + `exit 0`. - **NEW** `scripts/smoke-real.test.ts` — the driver itself: runs the loop for `SMOKE_CERT` (if set) and for the generated fixture; asserts signature validity, signer identity, validity dates; cross-checks with `openssl`. - **NEW** `scripts/vitest.smoke.config.ts` — minimal vitest config so the driver is runnable outside the app suite (root include is `src/**/*.test.ts`, node environment, no jsdom). - **MOD** `package.json` — one line: `"verify:real": "bash scripts/smoke-real.sh"` (mirrors the existing `verify:zip`). - **MOD** `README.md` — new section: headless run + the UI flow for both keystores. Explicitly **not** touched: `src/lib/**` (no library change — the loop must run on today's code), `test/**`, `src/test/**`, `Containerfile`, `Caddyfile`, `vite.config.ts`, anything under `original/`, `work/`, `.p12pass`, `.gitignore`. No new dependency. No PIN or cert bytes are ever printed, echoed or committed. ## Ordered tasks | # | Task | Verify with | |---|---|---| | 1 | Probe the real keystore read-only (absolute paths into the operator checkout, never copied) to see what `loadSignerSession` + `verifyArtifact` report, incl. the `certChain` check | `bash scripts/smoke-real.sh --cert <abs>/work/certs/current-cert.p12 --pin-file <abs>/.p12pass` (temp probe first) | | 2 | Write the driver + smoke vitest config + wrapper; loop for the generated fixture | `bash scripts/smoke-real.sh --generated` ⇒ exit 0, `openssl cms -verify` OK | | 3 | Wire the real certificate case and both-at-once default | `bash scripts/smoke-real.sh --cert <abs>/work/certs/current-cert.p12 --pin-file <abs>/.p12pass` ⇒ exit 0 | | 4 | Skip path (missing keystore) stays green | `bash scripts/smoke-real.sh --cert /tmp/does-not-exist.p12` ⇒ exit 0 + notice | | 5 | README: headless run + UI flow for each keystore | `pnpm exec vitest run` unaffected; re-read section | | 6 | Gates | `pnpm test`, `pnpm build`, `pnpm check`, `git diff --check` | Commit after each verified task: `<area>: <what> (Refs #39)`. ## Open questions → assumption taken 1. **`work/certs/` + `.p12pass` are gitignored and absent from this worktree** (they live only in the operator's checkout). Assumption: the wrapper defaults to repo-relative `work/certs/current-cert.p12` and `.p12pass`, skips with a notice when they are missing, and the real-keystore evidence for this issue is produced by pointing `--cert`/`--pin-file` at those **read-only absolute paths** (never copied into the worktree, never printed). The committed script keeps working for any user who drops a keystore in `work/certs/`. 2. **Chain trust.** `signCadesDetached` embeds only the signer certificate, and the bundled roots hold the FNMT root. The self-signed fixture's leaf is signed by the fixture CA, which is not a bundled root — so `certChain` may fail. Assumption: the driver asserts `report.ok` and supplies the container's own non-signer certificates as extra `trustedRoots` (which is what the existing verify tests do with the throwaway CA). If the FNMT chain still cannot be built from the bundled roots, the driver asserts every check *except* `certChain` and prints the `certChain` status and reason explicitly rather than hiding it. 3. **UI path evidence.** No browser automation is requested for #39 ("manual-evidence"), so the UI flow is documented as a screenshot-able numbered step list in README, generated from the real component names/labels in `src/lib/App.svelte` + `CertPicker.svelte`. ## Risk and fallback - **Risk:** the real p12 uses legacy PBES1 (3DES/RC2) and is rejected by the parser → the loop cannot complete and #39 would be blocked by #5's PBES2-only limit. Fallback: report the exact rejection message, keep the fixture case green, and report PARTIAL with the reason. - **Risk:** `pnpm check`/`svelte-check` picks up the new `scripts/*.ts` (tsconfig `include` covers `*`) and complains about Node globals. Fallback: the driver uses only `node:` imports and `process` (both typed by the already-installed `@types/node`). - **Risk:** vitest with a second config changes nothing about `pnpm test` (root `include` is `src/**/*.test.ts`), so the smoke file must not be matched by the app suite. Fallback: keep the driver under `scripts/` (outside `src/`) and confirm with a run of `pnpm test --reporter=basic`.
Author
Owner

: did not load at all — — because it is written the way the Windows/CryptoAPI exporter writes PKCS#12: BER indefinite lengths and chunked OCTET STRINGs (incl. the implicit of ). So + gained BER support, and gained (the smokes identity assertion caught the picker and the report printing different subjects).

Gates:

  • bash scripts/smoke-real.sh → exit 0, 2 passed. Real keystore (--cert <operator checkout>/work/certs/current-cert.p12 --pin-file <operator checkout>/.p12pass, read-only paths, never copied, PIN never printed): valid 2026-02-02T19:18:28.000Z → 2028-12-31T22:59:59.000Z, openssl cms -verify → CMS Verification successful. Generated fixture: same loop, same oracle result.
  • Skip path (no keystore): exit 0 + notice.
  • pnpm build clean, pnpm check 0 errors/0 warnings, git diff --check clean.
  • pnpm test: red, pre-existing. 6 failed/135 passed — all xmlsec1 printed no verdict (exit 0): Verification status: OK (installed xmlsec1 wording vs the helper in src/test/fixtures.ts) plus jsdom timing flakes in App.test.ts. Baseline with my three source files reverted: 5 failed, same cause, and three App.test.ts runs gave three different failing sets. Nothing deleted/skipped/weakened.

Two decisions for a human: (1) verifying your own fresh .csig still shows a red Trust chain card — signing embeds only the signer certificate (#6), so report.ok=false even for the real FNMT keystore whose container carries AC FNMT Usuarios; the smoke prints it and asserts the signature-level checks. (2) #40 proposes old-cert.p12 as this scripts default, but that keystore is expired so fails by design — the two issues need reconciling.
EOF
)

: did **not load at all** — — because it is written the way the Windows/CryptoAPI exporter writes PKCS#12: BER indefinite lengths and chunked OCTET STRINGs (incl. the implicit of ). So + gained BER support, and gained (the smokes identity assertion caught the picker and the report printing different subjects). Gates: - `bash scripts/smoke-real.sh` → **exit 0**, `2 passed`. Real keystore (`--cert <operator checkout>/work/certs/current-cert.p12 --pin-file <operator checkout>/.p12pass`, read-only paths, never copied, PIN never printed): `valid 2026-02-02T19:18:28.000Z → 2028-12-31T22:59:59.000Z`, `openssl cms -verify → CMS Verification successful`. Generated fixture: same loop, same oracle result. - Skip path (`no keystore`): `exit 0` + notice. - `pnpm build` clean, `pnpm check` 0 errors/0 warnings, `git diff --check` clean. - `pnpm test`: **red, pre-existing**. 6 failed/135 passed — all `xmlsec1 printed no verdict (exit 0): Verification status: OK` (installed xmlsec1 wording vs the helper in `src/test/fixtures.ts`) plus jsdom timing flakes in `App.test.ts`. Baseline with my three source files reverted: 5 failed, same cause, and three `App.test.ts` runs gave three different failing sets. Nothing deleted/skipped/weakened. Two decisions for a human: (1) verifying your own fresh `.csig` still shows a red *Trust chain* card — signing embeds only the signer certificate (#6), so `report.ok=false` even for the real FNMT keystore whose container carries `AC FNMT Usuarios`; the smoke prints it and asserts the signature-level checks. (2) #40 proposes `old-cert.p12` as this scripts default, but that keystore is expired so fails by design — the two issues need reconciling. EOF )
Author
Owner

DONE — branch issue/39-e2e-sign-verify-real-valid-fnm, commits d9eaa0e1 (BER PKCS#12), 426dbab3 (E2E smoke + README). Not pushed; report at .bullet/meta/issue-39.report.md.

Delivered: scripts/smoke-real.sh (--cert/--pin-file/--no-generated) + scripts/smoke-real.test.ts driving the app's own modules through load → CAdES-sign → verify, with two openssl oracles; pnpm verify:real; README section for the headless run and the matching UI flow; CI regression test for the real-world BER container shape.

Scope amendment (needed by this issue's own acceptance): work/certs/current-cert.p12 did not load at all — not-pkcs12: unsupported DER length — because it is written the way the Windows/CryptoAPI exporter writes PKCS#12: BER indefinite lengths and chunked OCTET STRINGs, including the implicit [0] of encryptedContent. So src/lib/asn1.ts + src/lib/pkcs12.ts gained BER support, and src/lib/verify/x509.ts gained 2.5.4.42 -> GN (the smoke's identity assertion caught the picker and the report printing different subjects for the same certificate).

Gates:

  • bash scripts/smoke-real.sh — exit 0, 2 passed. Real keystore (--cert <operator checkout>/work/certs/current-cert.p12 --pin-file <operator checkout>/.p12pass, read-only paths, never copied, PIN never printed): valid 2026-02-02T19:18:28.000Z -> 2028-12-31T22:59:59.000Z, openssl cms -verify -> CMS Verification successful. Generated fixture: same loop, same oracle result.
  • Skip path (no keystore): exit 0 + notice.
  • pnpm build clean, pnpm check 0 errors / 0 warnings, git diff --check clean.
  • pnpm test: red, pre-existing. 6 failed / 135 passed — all xmlsec1 printed no verdict (exit 0): Verification status: OK (installed xmlsec1 wording vs the helper in src/test/fixtures.ts) plus jsdom timing flakes in App.test.ts. Baseline with my three source files reverted: 5 failed, same cause, and three App.test.ts runs gave three different failing sets. Nothing deleted, skipped or weakened.

Two decisions for a human: (1) verifying your own fresh .csig still shows a red Trust chain card — signing embeds only the signer certificate (#6), so report.ok=false even for the real FNMT keystore whose container carries AC FNMT Usuarios; the smoke prints that honestly and asserts the signature-level checks. (2) #40 proposes old-cert.p12 as this script's default, but that keystore is expired so certValidity fails by design — the two issues need reconciling.

**DONE** — branch `issue/39-e2e-sign-verify-real-valid-fnm`, commits `d9eaa0e1` (BER PKCS#12), `426dbab3` (E2E smoke + README). Not pushed; report at `.bullet/meta/issue-39.report.md`. Delivered: `scripts/smoke-real.sh` (`--cert`/`--pin-file`/`--no-generated`) + `scripts/smoke-real.test.ts` driving the app's own modules through load → CAdES-sign → verify, with two `openssl` oracles; `pnpm verify:real`; README section for the headless run and the matching UI flow; CI regression test for the real-world BER container shape. Scope amendment (needed by this issue's own acceptance): `work/certs/current-cert.p12` did **not load at all** — `not-pkcs12: unsupported DER length` — because it is written the way the Windows/CryptoAPI exporter writes PKCS#12: BER indefinite lengths and chunked OCTET STRINGs, including the implicit `[0]` of `encryptedContent`. So `src/lib/asn1.ts` + `src/lib/pkcs12.ts` gained BER support, and `src/lib/verify/x509.ts` gained `2.5.4.42 -> GN` (the smoke's identity assertion caught the picker and the report printing different subjects for the same certificate). Gates: - `bash scripts/smoke-real.sh` — exit 0, 2 passed. Real keystore (`--cert <operator checkout>/work/certs/current-cert.p12 --pin-file <operator checkout>/.p12pass`, read-only paths, never copied, PIN never printed): `valid 2026-02-02T19:18:28.000Z -> 2028-12-31T22:59:59.000Z`, `openssl cms -verify -> CMS Verification successful`. Generated fixture: same loop, same oracle result. - Skip path (no keystore): exit 0 + notice. - `pnpm build` clean, `pnpm check` 0 errors / 0 warnings, `git diff --check` clean. - `pnpm test`: **red, pre-existing**. 6 failed / 135 passed — all `xmlsec1 printed no verdict (exit 0): Verification status: OK` (installed xmlsec1 wording vs the helper in `src/test/fixtures.ts`) plus jsdom timing flakes in `App.test.ts`. Baseline with my three source files reverted: 5 failed, same cause, and three `App.test.ts` runs gave three different failing sets. Nothing deleted, skipped or weakened. Two decisions for a human: (1) verifying your own fresh `.csig` still shows a red *Trust chain* card — signing embeds only the signer certificate (#6), so `report.ok=false` even for the real FNMT keystore whose container carries `AC FNMT Usuarios`; the smoke prints that honestly and asserts the signature-level checks. (2) #40 proposes `old-cert.p12` as this script's default, but that keystore is expired so `certValidity` fails by design — the two issues need reconciling.
Author
Owner

bullet: coder session died — relaunched from committed state (branch issue/39-e2e-sign-verify-real-valid-fnm).

bullet: coder session died — relaunched from committed state (branch `issue/39-e2e-sign-verify-real-valid-fnm`).
Author
Owner

Claimed — branch issue/39-e2e-sign-verify-real-valid-fnm. Doing: verify the smoke + plan already on the branch (real current-cert.p12 + generated fixture), re-run every gate, fix what is actually red, then report.

Claimed — branch `issue/39-e2e-sign-verify-real-valid-fnm`. Doing: verify the smoke + plan already on the branch (real `current-cert.p12` + generated fixture), re-run every gate, fix what is actually red, then report.
Author
Owner

DONE — branch issue/39-e2e-sign-verify-real-valid-fnm, commits d9eaa0e1, 426dbab3, 6b32e900, 1e44aba0, 1bee11ac, 93bdb5d8. Working tree clean, not pushed.

Gates (all on the final commit):

  • pnpm test — PASS: Tests 141 passed (141), 16 files, exit 0
  • pnpm build — PASS: Wrote site to "build", exit 0
  • pnpm check — PASS: 1324 FILES 0 ERRORS 0 WARNINGS
  • pnpm verify:real with the real keystore (read-only absolute --cert/--pin-file) — PASS, exit 0: Tests 2 passed (2); real CN=GARCIA APARICIO EDUARDO - 05718264G … C=ES, valid 2026-02-02 → 2028-12-31, openssl cms -verify → CMS Verification successful; generated CN=Ñandú Prueba …, same verdict
  • pnpm verify:real default paths (keystore absent in the worktree) — PASS, exit 0, skip notice
  • git diff --check — clean

Since the previous session's report: fixed the two causes of its red pnpm test — xmlsec1Verify now reads xmlsec1 1.3.12's Verification status: OK/FAILED (5 tests failed on the oracle's output; FAILED still maps to FAIL), and the jsdom WebCrypto waits got one shared ASYNC = { timeout: 15_000 } instead of testing-library's 1 s default (reproduced with this branch's src/lib reverted ⇒ predates #39; no assertion changed, nothing deleted or skipped). README UI steps now carry the Spanish labels the screenshots will show.

Left for a human: (1) the two gate fixes sit outside #39's literal scope — separable with one revert; (2) verifying your own fresh .csig shows a red Trust chain card because only the signer certificate is embedded (#6); (3) --cert work/certs/old-cert.p12 fails certValidity by design (#40). Full report: .bullet/meta/issue-39.report.md.

**DONE** — branch `issue/39-e2e-sign-verify-real-valid-fnm`, commits `d9eaa0e1`, `426dbab3`, `6b32e900`, `1e44aba0`, `1bee11ac`, `93bdb5d8`. Working tree clean, not pushed. Gates (all on the final commit): - `pnpm test` — PASS: `Tests 141 passed (141)`, 16 files, exit 0 - `pnpm build` — PASS: `Wrote site to "build"`, exit 0 - `pnpm check` — PASS: `1324 FILES 0 ERRORS 0 WARNINGS` - `pnpm verify:real` with the real keystore (read-only absolute `--cert`/`--pin-file`) — PASS, exit 0: `Tests 2 passed (2)`; real `CN=GARCIA APARICIO EDUARDO - 05718264G … C=ES`, valid `2026-02-02 → 2028-12-31`, `openssl cms -verify → CMS Verification successful`; generated `CN=Ñandú Prueba …`, same verdict - `pnpm verify:real` default paths (keystore absent in the worktree) — PASS, exit 0, skip notice - `git diff --check` — clean Since the previous session's report: fixed the two causes of its red `pnpm test` — `xmlsec1Verify` now reads xmlsec1 1.3.12's `Verification status: OK/FAILED` (5 tests failed *on the oracle's output*; `FAILED` still maps to `FAIL`), and the jsdom WebCrypto waits got one shared `ASYNC = { timeout: 15_000 }` instead of testing-library's 1 s default (reproduced with this branch's `src/lib` reverted ⇒ predates #39; no assertion changed, nothing deleted or skipped). README UI steps now carry the Spanish labels the screenshots will show. Left for a human: (1) the two gate fixes sit outside #39's literal scope — separable with one revert; (2) verifying your own fresh `.csig` shows a red *Trust chain* card because only the signer certificate is embedded (#6); (3) `--cert work/certs/old-cert.p12` fails `certValidity` by design (#40). Full report: `.bullet/meta/issue-39.report.md`.
Author
Owner

bullet: coder session died — relaunched from committed state (branch issue/39-e2e-sign-verify-real-valid-fnm).

bullet: coder session died — relaunched from committed state (branch `issue/39-e2e-sign-verify-real-valid-fnm`).
Author
Owner

DONE — re-verification session (session 3; sessions 1–2 died after committing and commenting). No new code: branch state is already the deliverable, every gate re-run and green.

Branch issue/39-e2e-sign-verify-real-valid-fnm, 6 commits, tree clean, not pushed:
d9eaa0e1 (BER PKCS#12) · 426dbab3 (E2E smoke + README) · 6b32e900 (xmlsec1 1.3.12 verdict) · 1e44aba0 · 1bee11ac (jsdom crypto waits) · 93bdb5d8 (README labels).

Gates (this session, on 93bdb5d8):

  • pnpm test — PASS Test Files 16 passed (16), Tests 141 passed (141), exit 0.
  • pnpm build — PASS Wrote site to "build", exit 0.
  • bash scripts/smoke-real.sh --cert <op-checkout>/work/certs/current-cert.p12 --pin-file <op-checkout>/.p12pass — PASS exit 0, Tests 2 passed (2). Real: CN=GARCIA APARICIO EDUARDO - 05718264G …, serial 0f51b50b…, valid 2026-02-02T19:18:28.000Z → 2028-12-31T22:59:59.000Z, openssl cms -verify → CMS Verification successful. Generated: CN=Ñandú Prueba, 2026-10-15 → 2027-10-15, same openssl verdict.
  • bash scripts/smoke-real.sh (keystore absent from this worktree) — PASS exit 0, skip notice + generated case runs.
  • git diff --check — clean.

Secrets: keystore read read-only via absolute --cert/--pin-file, nothing copied into the worktree; PIN scan of the tracked tree clean; no .p12 committed.

Known, reported as facts (unchanged from the previous report): certChain: fail on both keystores because signing embeds only the signer certificate (#6) — issuer certificate not included (FNMT) and self-signed root not in trust store (fixture), so report.ok=false. UI evidence is the README section only (issue asks for manual-evidence). --cert work/certs/old-cert.p12 fails certValidity by design — #40.

**DONE** — re-verification session (session 3; sessions 1–2 died after committing and commenting). No new code: branch state is already the deliverable, every gate re-run and green. Branch `issue/39-e2e-sign-verify-real-valid-fnm`, 6 commits, tree clean, not pushed: `d9eaa0e1` (BER PKCS#12) · `426dbab3` (E2E smoke + README) · `6b32e900` (xmlsec1 1.3.12 verdict) · `1e44aba0` · `1bee11ac` (jsdom crypto waits) · `93bdb5d8` (README labels). Gates (this session, on `93bdb5d8`): - `pnpm test` — PASS `Test Files 16 passed (16)`, `Tests 141 passed (141)`, exit 0. - `pnpm build` — PASS `Wrote site to "build"`, exit 0. - `bash scripts/smoke-real.sh --cert <op-checkout>/work/certs/current-cert.p12 --pin-file <op-checkout>/.p12pass` — PASS exit 0, `Tests 2 passed (2)`. Real: `CN=GARCIA APARICIO EDUARDO - 05718264G …`, serial `0f51b50b…`, valid `2026-02-02T19:18:28.000Z → 2028-12-31T22:59:59.000Z`, `openssl cms -verify → CMS Verification successful`. Generated: `CN=Ñandú Prueba`, `2026-10-15 → 2027-10-15`, same openssl verdict. - `bash scripts/smoke-real.sh` (keystore absent from this worktree) — PASS exit 0, skip notice + generated case runs. - `git diff --check` — clean. Secrets: keystore read read-only via absolute `--cert`/`--pin-file`, nothing copied into the worktree; PIN scan of the tracked tree clean; no `.p12` committed. Known, reported as facts (unchanged from the previous report): `certChain: fail` on both keystores because signing embeds only the signer certificate (#6) — `issuer certificate not included` (FNMT) and `self-signed root not in trust store` (fixture), so `report.ok=false`. UI evidence is the README section only (issue asks for manual-evidence). `--cert work/certs/old-cert.p12` fails `certValidity` by design — #40.
Author
Owner

bullet: coder session died — relaunched from committed state (branch issue/39-e2e-sign-verify-real-valid-fnm).

bullet: coder session died — relaunched from committed state (branch `issue/39-e2e-sign-verify-real-valid-fnm`).
Author
Owner

bullet: implementation finished (DONE: — re-verification session (session 3; sessions 1–2 died after committing and commenting). No new code: branch state is already the deliverable, every gate re-run and green.) — moving to review.

bullet: implementation finished (DONE: — re-verification session (session 3; sessions 1–2 died after committing and commenting). No new code: branch state is already the deliverable, every gate re-run and green.) — moving to review.
Author
Owner

Claimed — branch issue/39-e2e-sign-verify-real-valid-fnm. Doing: session 4 re-verification — re-ran pnpm test/pnpm build/scripts/smoke-real.sh (both keystores, exit 0) and walked the UI path in a real Chromium tab to check the manual-evidence steps; one doc fix committed (e2800d72) because the README named controls the app does not show.

Claimed — branch `issue/39-e2e-sign-verify-real-valid-fnm`. Doing: session 4 re-verification — re-ran `pnpm test`/`pnpm build`/`scripts/smoke-real.sh` (both keystores, exit 0) and walked the UI path in a real Chromium tab to check the manual-evidence steps; one doc fix committed (e2800d72) because the README named controls the app does not show.
Author
Owner

DONE — branch issue/39-e2e-sign-verify-real-valid-fnm, tip commit e2800d72 (this session's only commit: README manual-evidence steps now name the controls the app really shows — no Certificado label, card is Seleccionar firma, file picker is Agregar firma, no algorithm string in the signed row). Sessions 1–3's commits stand: d9eaa0e1, 426dbab3, 6b32e900, 1e44aba0, 1bee11ac, 93bdb5d8. Not pushed.

Gates: pnpm test exit 0 (16 files / 141 tests) · pnpm build exit 0 (static, Wrote site to "build") · bash scripts/smoke-real.sh --cert <op>/work/certs/current-cert.p12 --pin-file <op>/.p12pass exit 0, 2/2 pass, openssl cms -verify successful on both keystores (real: valid 2026-02-02 → 2028-12-31) · missing-keystore skip path exit 0 · git diff --check exit 0.

UI path walked in a real Chromium tab (generated fixture p12, temp, deleted): load → sign → .csig (1520 B) verified by openssl → verify panel shows the seven checks, content digest passes once the document is attached. Store-save round-trip not exercised; README no longer claims more than was observed.

Human call: certChain: fail on both keystores is expected (#6 embeds only the signer cert), so the smoke asserts the six signature checks and prints the chain as it comes — chain embedding is a separate change.

**DONE** — branch `issue/39-e2e-sign-verify-real-valid-fnm`, tip commit `e2800d72` (this session's only commit: README manual-evidence steps now name the controls the app really shows — no *Certificado* label, card is **Seleccionar firma**, file picker is **Agregar firma**, no algorithm string in the signed row). Sessions 1–3's commits stand: `d9eaa0e1`, `426dbab3`, `6b32e900`, `1e44aba0`, `1bee11ac`, `93bdb5d8`. Not pushed. Gates: `pnpm test` exit 0 (16 files / 141 tests) · `pnpm build` exit 0 (static, Wrote site to "build") · `bash scripts/smoke-real.sh --cert <op>/work/certs/current-cert.p12 --pin-file <op>/.p12pass` exit 0, 2/2 pass, `openssl cms -verify` successful on both keystores (real: valid 2026-02-02 → 2028-12-31) · missing-keystore skip path exit 0 · `git diff --check` exit 0. UI path walked in a real Chromium tab (generated fixture p12, temp, deleted): load → sign → `.csig` (1520 B) verified by openssl → verify panel shows the seven checks, content digest passes once the document is attached. Store-save round-trip not exercised; README no longer claims more than was observed. Human call: `certChain: fail` on both keystores is expected (#6 embeds only the signer cert), so the smoke asserts the six signature checks and prints the chain as it comes — chain embedding is a separate change.
Author
Owner

bullet: reviewer launched (model 9router/reviewer, independent of coder).

bullet: reviewer launched (model `9router/reviewer`, independent of coder).
Author
Owner

REVIEW: FIXED 1 — commit e2800d72: findings: src/lib/asn1.ts indefinite-length BER flattening matched chunked strings by tag number only, so a context tag whose number collides (certBag/safeContents [3]/[4], GeneralName-style tags) was rewritten into a chunked primitive and lost its children — fixed in 797fbe6c; UNFIXED: none

REVIEW: FIXED 1 — commit e2800d72: findings: src/lib/asn1.ts indefinite-length BER flattening matched chunked strings by tag number only, so a context tag whose number collides (certBag/safeContents [3]/[4], GeneralName-style tags) was rewritten into a chunked primitive and lost its children — fixed in 797fbe6c; UNFIXED: none
Author
Owner

bullet: fixer round done — fresh reviewer re-checking.

bullet: fixer round done — fresh reviewer re-checking.
Author
Owner

REVIEW: FIXED 3 — commit 76c57d79: findings: (1) plan Scope promised a --generated flag on scripts/smoke-real.sh that was missing — added, commit 76c57d79; (2) the smoke driver leaked the generated fixture temp dir (325 autofirme-p12-* dirs accumulated) — cleaned in afterAll, commit 76c57d79; (3) README UI step for the generated fixture gave no way to materialize modern.p12 and misdescribed the expired flag as amber (it is text-destructive red) — one-liner + wording fixed, commit 76c57d79; UNFIXED: report gate row says "2/2 test files pass" but the run is 1 file / 2 tests (cosmetic wording in the implementer report, evidence block itself is correct)

REVIEW: FIXED 3 — commit 76c57d79: findings: (1) plan Scope promised a `--generated` flag on scripts/smoke-real.sh that was missing — added, commit 76c57d79; (2) the smoke driver leaked the generated fixture temp dir (325 autofirme-p12-* dirs accumulated) — cleaned in afterAll, commit 76c57d79; (3) README UI step for the generated fixture gave no way to materialize modern.p12 and misdescribed the expired flag as amber (it is text-destructive red) — one-liner + wording fixed, commit 76c57d79; UNFIXED: report gate row says "2/2 test files pass" but the run is 1 file / 2 tests (cosmetic wording in the implementer report, evidence block itself is correct)
Author
Owner

bullet: fixer launched (round 1/2).

bullet: fixer launched (round 1/2).
Author
Owner

FIXED: applied 4 — 76c57d79 wire --generated (plan flag pair) + regression 4c7e7e9c scripts/smoke-generated-flag.test.ts (stub-pnpm drives the wrapper: --generated reaches the runner with SMOKE_GENERATED=1, --no-generated keeps it off, last flag wins; pre-fix wrapper exits 2 on --generated); 76c57d79 README fixture step + "amber"→"red" + regression 53c54ea9 scripts/readme-fixture-command.test.ts (extracts the documented node one-liner from README.md itself, runs it in a private TMPDIR, asserts the printed modern.p12 exists; CertPicker.svelte:283 uses text-destructive = red); 76c57d79 fixture-dir cleanup + regression 889aec9f scripts/smoke-fixture-leak.test.ts (nested driver run, $TMPDIR autofirme-p12-* count identical before/after: 337→337; pre-fix has no afterAll); MINOR report gate wording ("2/2 test files" → 1 file / 2 tests driver, 4 files / 7 tests with the new regressions) corrected in the fixer report — the report is the deliverable, no repo commit applies. Gates re-run: pnpm test 16 files/142 tests exit 0; pnpm check 0 errors; pnpm build exit 0; smoke-real.sh (both keystores) exit 0, 4 files/7 tests, openssl cms -verify successful on FNMT + generated; skip paths exit 0; git diff --check clean; no secrets tracked. Nothing weakened, no new deps.

FIXED: applied 4 — 76c57d79 wire --generated (plan flag pair) + regression 4c7e7e9c scripts/smoke-generated-flag.test.ts (stub-pnpm drives the wrapper: --generated reaches the runner with SMOKE_GENERATED=1, --no-generated keeps it off, last flag wins; pre-fix wrapper exits 2 on --generated); 76c57d79 README fixture step + "amber"→"red" + regression 53c54ea9 scripts/readme-fixture-command.test.ts (extracts the documented node one-liner from README.md itself, runs it in a private TMPDIR, asserts the printed modern.p12 exists; CertPicker.svelte:283 uses text-destructive = red); 76c57d79 fixture-dir cleanup + regression 889aec9f scripts/smoke-fixture-leak.test.ts (nested driver run, $TMPDIR autofirme-p12-* count identical before/after: 337→337; pre-fix has no afterAll); MINOR report gate wording ("2/2 test files" → 1 file / 2 tests driver, 4 files / 7 tests with the new regressions) corrected in the fixer report — the report is the deliverable, no repo commit applies. Gates re-run: pnpm test 16 files/142 tests exit 0; pnpm check 0 errors; pnpm build exit 0; smoke-real.sh (both keystores) exit 0, 4 files/7 tests, openssl cms -verify successful on FNMT + generated; skip paths exit 0; git diff --check clean; no secrets tracked. Nothing weakened, no new deps.
Author
Owner

bullet: reviewer launched (model 9router/reviewer, independent of coder).

bullet: reviewer launched (model `9router/reviewer`, independent of coder).
Author
Owner

REVIEW: FIXED 1 — commit e7610b7d: findings: scripts/smoke-fixture-leak.test.ts snapshots the $TMPDIR autofirme-p12-* count while scripts/smoke-real.test.ts creates/removes the same prefix in a parallel sibling file (vitest default fileParallelism) — latent flake; fixed serially in e7610b7d and re-verified (5 straight full-smoke passes, 4 files / 7 tests); UNFIXED: none

REVIEW: FIXED 1 — commit e7610b7d: findings: scripts/smoke-fixture-leak.test.ts snapshots the $TMPDIR autofirme-p12-* count while scripts/smoke-real.test.ts creates/removes the same prefix in a parallel sibling file (vitest default fileParallelism) — latent flake; fixed serially in e7610b7d and re-verified (5 straight full-smoke passes, 4 files / 7 tests); UNFIXED: none
Author
Owner

bullet: fixer launched (round 2/2).

bullet: fixer launched (round 2/2).
Author
Owner

FIXED: applied 0 — round-2 finding 9 (leak-count race: scripts/smoke-fixture-leak.test.ts counts $TMPDIR autofirme-p12-* while scripts/smoke-real.test.ts creates/removes the same prefix under vitest fileParallelism) was already fixed by reviewer commit e7610b7d (fileParallelism: false, scripts/vitest.smoke.config.ts:20), which is the branch head — verified here, no new commits needed: full smoke with the real FNMT keystore (--cert work/certs/current-cert.p12 --pin-file .p12pass) exit 0 twice + 3 further default smoke runs exit 0 — 5 consecutive 4-file/7-test passes, zero flake; pnpm test exit 0 — 16 files, 142 tests; pnpm check exit 0 — 0 errors/0 warnings; git status clean, git diff --check clean, git ls-files '*.p12' '.p12pass' → none tracked. UNFIXED: none

FIXED: applied 0 — round-2 finding 9 (leak-count race: scripts/smoke-fixture-leak.test.ts counts $TMPDIR autofirme-p12-* while scripts/smoke-real.test.ts creates/removes the same prefix under vitest fileParallelism) was already fixed by reviewer commit e7610b7d (fileParallelism: false, scripts/vitest.smoke.config.ts:20), which is the branch head — verified here, no new commits needed: full smoke with the real FNMT keystore (--cert work/certs/current-cert.p12 --pin-file .p12pass) exit 0 twice + 3 further default smoke runs exit 0 — 5 consecutive 4-file/7-test passes, zero flake; pnpm test exit 0 — 16 files, 142 tests; pnpm check exit 0 — 0 errors/0 warnings; git status clean, git diff --check clean, git ls-files '*.p12' '.p12pass' → none tracked. UNFIXED: none
Author
Owner

bullet: fixer round done — fresh reviewer re-checking.

bullet: fixer round done — fresh reviewer re-checking.
Author
Owner

REVIEW: CLEAN — commit e7610b7d: findings none

REVIEW: CLEAN — commit e7610b7d: findings none
Author
Owner

bullet: review CLEAN — ready to publish.

bullet: review CLEAN — ready to publish.
Author
Owner

bullet: branch issue/39-e2e-sign-verify-real-valid-fnm not on origin — cannot integrate.

bullet: branch `issue/39-e2e-sign-verify-real-valid-fnm` not on origin — cannot integrate.
Author
Owner

bullet: PR #54 open → bullet-develop (auto-merge follows).

bullet: PR #54 open → `bullet-develop` (auto-merge follows).
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

Claimed — branch issue/39-e2e-sign-verify-real-valid-fnm. Doing: re-apply #39 on top of bullet-develop and reconcile conflicts.

Claimed — branch `issue/39-e2e-sign-verify-real-valid-fnm`. Doing: re-apply #39 on top of bullet-develop and reconcile conflicts.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merge conflicts with bullet-develop — conflict-solver agent owns the worktree until it pushes.

bullet: merge conflicts with `bullet-develop` — conflict-solver agent owns the worktree until it pushes.
Author
Owner

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into bullet-develop; PR auto-merges next tick.

bullet: merge blocked (HTTP 405) — conciliation agent merges trunk work into `bullet-develop`; PR auto-merges next tick.
Author
Owner

bullet: merged into bullet-develop — integration gates green.

bullet: merged into `bullet-develop` — integration gates green.
Author
Owner

bullet: PR #54 closed — work already merged into bullet-develop.

bullet: PR #54 closed — work already merged into `bullet-develop`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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
EduarD3V/AutoFirme#39
No description provided.