Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
161 commits
Select commit Hold shift + click to select a range
2c5c409
docs(encryption-suites): spec — harden vault key-material guards (#673)
rjzondervan Sep 10, 2026
455fb54
docs(encryption-suites): spec — migrate emergency access on rotation …
rjzondervan Sep 10, 2026
ea787dc
docs(encryption-suites): reconcile 673 open questions with #674
rjzondervan Sep 10, 2026
8965d7b
docs(emergency-access): fold the destructive-revocation warning into …
rjzondervan Sep 10, 2026
3bc32e9
docs(encryption-suites): abort deletes the successor, not revokes it …
rjzondervan Sep 10, 2026
49ff8c2
feat(encryption-suites): add the migration abort route (#673)
rjzondervan Sep 10, 2026
bb0e61c
feat(vault-key-proof): server-verified master-password guard (#673)
rjzondervan Sep 10, 2026
2d445d4
feat(vault-key-proof): client proof + wire the password-in-hand flows…
rjzondervan Sep 10, 2026
0c65767
feat(vault-key-proof): wire the emergency and completion flows (#673,…
rjzondervan Sep 10, 2026
9c257c2
test(vault-key-proof): cross-impl round-trip + guard docs (#673, §6.3…
rjzondervan Sep 10, 2026
7e65e05
test(emergency-access): cover the guarded revoke confirm dialog (#673)
rjzondervan Sep 11, 2026
0976e54
fix(quality): satisfy phpcs, phpmd and l10n gates on the guard (#673)
rjzondervan Sep 11, 2026
82af293
test(vault-key-proof): cover proofChallenge, countCommitted, abort li…
rjzondervan Sep 11, 2026
f0931d3
fix(l10n): rebuild the browser catalogues for the new keys (#673)
rjzondervan Sep 11, 2026
7dae235
fix(keepiq): satisfy hydra gates 13/16/46 on the vault-key-proof guar…
rjzondervan Sep 11, 2026
d9072e3
chore(674): stack on harden-vault-key-material-guards (#673)
rjzondervan Sep 11, 2026
96470c1
fix(keepiq): order v-model:password before other bindings on the revo…
rjzondervan Sep 11, 2026
e34b171
chore(674): pull the revoke-dialog attribute-order lint fix from #673
rjzondervan Sep 11, 2026
664993e
feat(keepiq): re-point emergency-access envelopes during suite rotati…
rjzondervan Sep 11, 2026
a009f11
feat(keepiq): refuse silent emergency-access loss on suite revocation…
rjzondervan Sep 11, 2026
c5564c7
feat(keepiq): migrate emergency access in the browser and warn on rev…
rjzondervan Sep 11, 2026
11de95c
docs(674): mark migrate-emergency-access-on-rotation tasks complete
rjzondervan Sep 11, 2026
63dc7df
fix(674): restore handleRevoke's @spec docblock and prettier-format t…
rjzondervan Sep 11, 2026
96b7dab
chore(release): sync main back into development
github-actions[bot] Sep 12, 2026
7493b37
chore(release): 0.3.4-unstable.20260912202807
github-actions[bot] Sep 12, 2026
cf2012e
Merge pull request #690 from ConductionNL/release/v0.3.4-unstable.202…
rubenvdlinde Sep 13, 2026
a5e1297
Merge pull request #683 from ConductionNL/sync/main-to-development-0.3.2
rubenvdlinde Sep 13, 2026
c818ee2
chore(sync): carry beta back into development
github-actions[bot] Sep 13, 2026
0309fcc
Merge pull request #695 from ConductionNL/sync/beta-to-development-20…
rubenvdlinde Sep 13, 2026
aef04a7
fix(keepiq): guard suite revocation with a vault-key proof (#673)
rjzondervan Sep 14, 2026
74b633c
fix(keepiq): bind complete's loss-acknowledgement params to the proof…
rjzondervan Sep 14, 2026
fc0005c
Merge remote-tracking branch 'origin/development' into feature/673/ha…
rjzondervan Sep 14, 2026
cc63def
style(keepiq): tighten boundParam's @param type (#673)
rjzondervan Sep 14, 2026
752277c
fix(keepiq): count re-enveloped contacts in the abort gate (#674)
rjzondervan Sep 14, 2026
7ae3f99
Merge remote-tracking branch 'origin/feature/673/harden-vault-key-mat…
rjzondervan Sep 14, 2026
716b8b6
fix(keepiq): make the revoke-suite proof purpose issuable (#673)
rjzondervan Sep 14, 2026
04d3d14
Merge remote-tracking branch 'origin/feature/673/harden-vault-key-mat…
rjzondervan Sep 14, 2026
225eada
fix(keepiq): address Wilco's three #678 should-fixes (#674)
rjzondervan Sep 14, 2026
e097a21
fix(encryption): single-line revoke vault-key-proof attribute for gat…
rjzondervan Sep 14, 2026
d46a2e4
Merge pull request #677 from ConductionNL/feature/673/harden-vault-ke…
rjzondervan Sep 14, 2026
8504024
Merge remote-tracking branch 'origin/development' into feature/674/mi…
rjzondervan Sep 14, 2026
3b63d30
Merge pull request #678 from ConductionNL/feature/674/migrate-emergen…
rjzondervan Sep 14, 2026
7d1f0a7
docs(encryption-suites): scaffold admin-suite-revocation change + ADR…
rjzondervan Sep 14, 2026
8fc72f8
feat(encryption-suites): admin force-revoke endpoint + compromise cas…
rjzondervan Sep 14, 2026
cc3b2a0
feat(encryption-suites): admin Encryption-suites settings section
rjzondervan Sep 14, 2026
df89d9a
docs(encryption-suites): mark admin-suite-revocation tasks 1-19 done
rjzondervan Sep 14, 2026
d24c0c5
fix(encryption-suites): null-check the acting admin in forceRevoke
rjzondervan Sep 14, 2026
b9367ba
fix(waivers): drop the dead deployment excuse from nine emergency-acc…
rubenvdlinde Sep 14, 2026
0ca3273
i18n(encryption-suites): translate admin force-revoke strings into al…
rjzondervan Sep 15, 2026
6a319c9
chore(deps): advertise Nextcloud 35 support (max-version 34 -> 35)
rjzondervan Sep 15, 2026
d3ae62a
feat(connections): show keepiq's breach check and SIEM export through…
rubenvdlinde Sep 15, 2026
67f8e03
Merge development into feature/702 — resolve l10n conflicts
rjzondervan Sep 15, 2026
73b3288
style(tests): prettier-format integrations-page e2e spec
rjzondervan Sep 15, 2026
5973a2d
build(deps): bump @conduction/nextcloud-vue to 3.x (lazy-loads Dexie)
rjzondervan Sep 15, 2026
0cb6ba6
style(e2e): format the integrations page spec so the format check pas…
rubenvdlinde Sep 15, 2026
8422d4c
fix(tests): two #706 tests that could not pass in CI (#717)
rubenvdlinde Sep 15, 2026
b21e44c
fix(tests): declare the classes the connection report tests use (#719)
rubenvdlinde Sep 16, 2026
0cc8f41
feat(connections): the breach check reads Switched off, and the forma…
rubenvdlinde Sep 16, 2026
74b5733
style(tests): format the two connection specs #721 left unformatted (…
rubenvdlinde Sep 16, 2026
99302bf
Merge remote-tracking branch 'origin/development' into feature/keepiq…
rjzondervan Sep 16, 2026
5f871e5
Merge remote-tracking branch 'origin/development' into feature/keepiq…
rjzondervan Sep 16, 2026
f83d592
Merge remote-tracking branch 'origin/development' into feature/702/ad…
rjzondervan Sep 16, 2026
a99364f
Merge pull request #703 from ConductionNL/feature/702/admin-suite-rev…
rjzondervan Sep 17, 2026
9def1c2
ci(matrix): derive the Nextcloud test matrix from info.xml
rjzondervan Sep 17, 2026
eb65a4f
fix(breach): a failed lookup logs the outcome, never the prefix (#727)
rubenvdlinde Sep 18, 2026
d8cceeb
build(deps-dev): bump conduction/hydra-gates from 1.16.1 to 1.18.0 (#…
dependabot[bot] Sep 18, 2026
f2e18e0
build(deps): bump web-token/jwt-library from 4.2.2 to 4.2.3 (#715)
dependabot[bot] Sep 18, 2026
5f1c552
build(deps-dev): bump nextcloud/ocp from 34.0.3 to 35.0.0 (#714)
dependabot[bot] Sep 18, 2026
94b901e
build(deps-dev): bump phpstan/phpstan from 2.2.13 to 2.2.14 (#713)
dependabot[bot] Sep 18, 2026
18c05e1
chore(deps): move nextcloud-vue back to the 2.x line
rubenvdlinde Sep 22, 2026
602fa26
Merge pull request #731 from ConductionNL/chore/nextcloud-vue-back-to-2x
rubenvdlinde Sep 22, 2026
e473d97
Merge development into feature/keepiq-nc35-support
rjzondervan Sep 24, 2026
f0705e9
test(nc35): port the migration double to ITable, and re-point the mat…
rjzondervan Sep 24, 2026
9bf78f6
fix(apphost): register OpenRegister's prefix without private API
rjzondervan Sep 24, 2026
7921095
Merge the OpenRegister autoload fix into the NC35 branch
rjzondervan Sep 24, 2026
931c81d
fix(ci): the three checks the autoload change broke
rjzondervan Sep 24, 2026
771be92
fix(test): decide the table double from the signature, not the autolo…
rjzondervan Sep 24, 2026
42b874b
test(apphost): cover unregister() and register()'s short-circuit
rjzondervan Sep 24, 2026
7f8e256
test(apphost): actually exercise the loader, not just its address cal…
rjzondervan Sep 24, 2026
c887fd7
build(deps-dev): bump twig/twig from 3.28.0 to 3.29.0 (#733)
dependabot[bot] Sep 25, 2026
fcff1b4
build(deps): bump ramsey/uuid from 4.9.3 to 4.9.4 (#732)
dependabot[bot] Sep 25, 2026
5597579
fix(apphost): skip a disabled OpenRegister and log prelude failures once
rjzondervan Sep 25, 2026
1777758
test(appinfo): make the derived-matrix floor tests say what they check
rjzondervan Sep 25, 2026
4ef90e9
docs: refresh stale phpstan and info.xml comments
rjzondervan Sep 25, 2026
7feb575
fix(apphost): close the silent paths the prelude still had
rjzondervan Sep 25, 2026
d3b0451
chore: drop the dead OC_App psalm suppression and a stale matrix note
rjzondervan Sep 25, 2026
dbb9035
fix(deps): move dexie to 4.4.6 with openregister and the fleet (#735)
rubenvdlinde Sep 26, 2026
168cf68
feat(parity): seed the capability matrix with 191 rows in 12 areas
rubenvdlinde Sep 26, 2026
8e314af
feat(parity): fold first reader packs (work in progress)
rubenvdlinde Sep 26, 2026
bdbe373
feat(parity): fold partial r1, r2 and r3 packs (work in progress)
rubenvdlinde Sep 26, 2026
d05b0af
feat(parity): fold r3 pack, audit and apps areas complete (work in pr…
rubenvdlinde Sep 26, 2026
5425a32
feat(parity): fold r2 pack, sharing, requests and pki areas complete …
rubenvdlinde Sep 26, 2026
fa9c75a
feat(parity): fold r1 pack, vault and crypto areas complete (work in …
rubenvdlinde Sep 26, 2026
1d865f7
feat(parity): fold r4 pack, health, rotation and admin areas complete…
rubenvdlinde Sep 26, 2026
a2f3eb0
feat(parity): fold r5 pack and c1 progress (work in progress)
rubenvdlinde Sep 26, 2026
2de5622
feat(parity): fold c1 pack, Bitwarden and 1Password columns rated fro…
rubenvdlinde Sep 26, 2026
243c555
fix(parity): map sibling providers to dictionary keys and declare the…
rubenvdlinde Sep 26, 2026
f10253b
feat(parity): fold c2 pack, Passbolt and Nextcloud Passwords columns …
rubenvdlinde Sep 26, 2026
eb5071b
feat(parity): fold c3 pack, all 191 rows rated for keepiq and six com…
rubenvdlinde Sep 26, 2026
06f25bf
Merge pull request #737 from ConductionNL/feat/parity-capability-matrix
rubenvdlinde Sep 26, 2026
752d4b3
fix(parity): apply round 3 cross-lane corrections
rubenvdlinde Sep 26, 2026
9b7fe70
Merge pull request #757 from ConductionNL/fix/parity-corrections-round3
rubenvdlinde Sep 26, 2026
1569544
fix(parity): drop the stale #184 gap from the sharing-11 note
rubenvdlinde Sep 26, 2026
c88ff04
Merge pull request #759 from ConductionNL/fix/parity-corrections-final
rubenvdlinde Sep 26, 2026
39d7ebd
feat(parity): source-read packs for Bitwarden and Vault, 20 demand-si…
rubenvdlinde Sep 26, 2026
8f18b05
feat(parity): fold reader packs, 1Password and Keeper cells for the m…
rubenvdlinde Sep 26, 2026
7bf7122
feat(parity): fold the Vault source read, 87 of 88 structural unknown…
rubenvdlinde Sep 26, 2026
02a6005
feat(parity): more 1Password and Keeper cells from vendor docs (work …
rubenvdlinde Sep 26, 2026
248fc4d
feat(parity): fold the partial Bitwarden and Passbolt packs left by t…
rubenvdlinde Sep 26, 2026
93fc7ad
fix(parity): admin-18 built state is building, the schema has no partial
rubenvdlinde Sep 26, 2026
34d2c9a
feat(parity): Bitwarden new-row pack, eight rows from its source read…
rubenvdlinde Sep 26, 2026
b3228a1
feat(parity): Vault cells for the eight rows from the Bitwarden sourc…
rubenvdlinde Sep 26, 2026
3a71a2b
feat(parity): Passbolt source read folded, three feature-request rows…
rubenvdlinde Sep 26, 2026
9ee717e
feat(parity): Nextcloud Passwords source read folded, four rows from …
rubenvdlinde Sep 26, 2026
48cea4b
Merge pull request #761 from ConductionNL/parity/w5-keepiq-source-reads
rubenvdlinde Sep 26, 2026
ff7f253
chore(deps): move @conduction/nextcloud-vue to 2.57.1 (Dexie loads on…
rubenvdlinde Sep 27, 2026
4c214a9
chore(deps): relax the dexie pin now that nextcloud-vue loads it lazi…
rubenvdlinde Sep 27, 2026
b165a43
docs(openspec): OpenSpec pass batch 1 of 3, keepiq gap decisions and …
rubenvdlinde Sep 27, 2026
58cd98f
docs(openspec): OpenSpec pass batch 2 of 3, ten keepiq admin and apps…
rubenvdlinde Sep 27, 2026
b5727e0
docs(openspec): OpenSpec pass batch 3 of 3, ten keepiq audit, clients…
rubenvdlinde Sep 27, 2026
2fa085c
docs(parity): corrections round 8, 4 rows re-read against their issue…
rubenvdlinde Sep 28, 2026
124849b
chore(docs): remove the dead docusaurus/ site and the committed build…
rubenvdlinde Sep 28, 2026
a562c4c
fix(emergency-access): stop a stolen session from reaching the new pr…
rjzondervan Sep 28, 2026
1f53c25
fix(gdpr): require a vault-key proof to delete all keepiq data
rjzondervan Sep 28, 2026
55be44b
fix(encryption-suites): warn the owners of shared secrets on a compro…
rjzondervan Sep 28, 2026
26d2484
fix(apphost): move the AppHost wiring into a seam the tests can reach
rjzondervan Sep 28, 2026
26b6ee1
chore(l10n): regenerate the .js catalogs for the emergency-carry strings
rjzondervan Sep 28, 2026
5316f77
style: prettier and component order for the emergency-carry changes
rjzondervan Sep 28, 2026
e9c3b5b
Merge pull request #712 from ConductionNL/feature/keepiq-nc35-support
rjzondervan Sep 28, 2026
2cfb1e8
fix(cli): read the envelope the server really sends (#807)
rubenvdlinde Sep 28, 2026
9d18a8b
fix(quality): single-line key-proof attributes so gate-16 reads the @…
rjzondervan Sep 28, 2026
afb30e6
fix(encryption-suites): refuse to revoke a suite that is part of an i…
rjzondervan Sep 28, 2026
53003aa
fix(secrets): refuse a folder the secret's owner does not own (#810)
rubenvdlinde Sep 28, 2026
f2dc6ab
fix(import): restore every secret type from a backup (#812)
rubenvdlinde Sep 28, 2026
fb90ffe
fix(export): say how many secrets could not be decrypted before expor…
rubenvdlinde Sep 28, 2026
3ac559a
fix(encryption-suites): a compromise force-revoke ends an open migrat…
rjzondervan Sep 28, 2026
45d2d9f
fix(encryption-suites): the compromise warning names the secret, and …
rjzondervan Sep 28, 2026
d853836
Merge development into fix/800-emergency-and-gdpr-key-proof
rjzondervan Sep 28, 2026
37e1571
fix(vault-key-proof): single-use proofs, logged refusals, and a new-s…
rjzondervan Sep 28, 2026
d7787b4
fix(emergency-access): new-key re-envelope proof, residual reasons, d…
rjzondervan Sep 29, 2026
1fbc3e6
fix(encryption-suites): revoke both ends before ending the migration,…
rjzondervan Sep 29, 2026
a76742f
fix(secret-requests): a request on a suite that is no longer active c…
rjzondervan Sep 29, 2026
8b122cd
fix(sharing): stamp and flag the shared source on a compromise migration
rjzondervan Sep 29, 2026
3cabf5a
fix(emergency-access): record why a rotation did not carry a contact
rjzondervan Sep 29, 2026
08559e4
Merge remote-tracking branch 'origin/development' into fix/803-force-…
rjzondervan Sep 29, 2026
e443f72
fix(emergency-access): no re-establish nudge for uncarried contacts; …
rjzondervan Sep 29, 2026
3b1220d
Merge pull request #805 from ConductionNL/fix/802-compromise-cascade-…
rjzondervan Sep 29, 2026
b10c0d9
test(secret-requests): pin the compromise-termination chain; require …
rjzondervan Sep 29, 2026
51b4c2e
fix(emergency-access): no Re-establish in the standing view after a r…
rjzondervan Sep 29, 2026
c093cd6
Merge pull request #809 from ConductionNL/fix/803-force-revoke-in-pro…
rjzondervan Sep 29, 2026
4dda591
fix(emergency-access): a resumed rotation reports the contacts it rem…
rjzondervan Sep 29, 2026
4aa7aa1
Merge remote-tracking branch 'origin/development' into fix/800-emerge…
rjzondervan Sep 29, 2026
d3a1040
fix(emergency-access): tell the owner which contacts a rotation remov…
rjzondervan Sep 29, 2026
05a9b3a
fix(review): a resumed rotation that ends by accepting losses never r…
WilcoLouwerse Sep 29, 2026
f4cf3e0
fix(review): after a form Retry the note says "was resumed" and the u…
WilcoLouwerse Sep 29, 2026
40e9bc0
docs(review): spec the read-back after a resumed rotation is finished…
WilcoLouwerse Sep 29, 2026
1472a0a
fix(review): a failed read-back tells the owner nothing, and the test…
WilcoLouwerse Sep 29, 2026
566ee14
docs(review): copies of the pre-#800 "prompt to re-establish exactly …
WilcoLouwerse Sep 29, 2026
7227dee
fix(review): keep a break-glass started during a pending loss visible…
WilcoLouwerse Sep 29, 2026
d5328c8
Merge pull request #824 from ConductionNL/review/pr-804-fixes-r5
rjzondervan Sep 29, 2026
5b729df
Merge pull request #804 from ConductionNL/fix/800-emergency-and-gdpr-…
rjzondervan Sep 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
82 changes: 51 additions & 31 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,38 +118,52 @@ jobs:
app-name: keepiq
php-version: "8.3"
php-test-versions: '["8.3", "8.4"]'
# Order matters: the PHPUnit matrix uses the whole list, but the E2E
# (Playwright), Newman and Journeydoc jobs all check out
# `fromJSON(nextcloud-test-refs)[0]` as their single server. That server
# has to be one OpenRegister can load, because `additional-apps` below
# installs it and keepiq's AppHost integration delegates to it —
# OpenRegister's lib/ContextChat/ContentProvider.php implements
# `OCP\ContextChat\IContentProvider`, which exists in stable32 and NOT in
# stable31 (verified: raw.githubusercontent.com returns 404 for the
# stable31 path). On stable31 every `occ` invocation printed
# NO `nextcloud-test-refs` HERE, DELIBERATELY. The shared workflow derives
# the matrix from appinfo/info.xml when this input is unset, and a derived
# matrix cannot disagree with the declared range. The override that used to
# sit here is exactly how the two came apart: it read
# `["stable34", "stable32", "stable33"]` while this branch moved info.xml to
# `<nextcloud min-version="32" max-version="35"/>`, so NC 35 was advertised
# to the App Store with no job touching it — not known-broken, unmeasured.
# gate-65 rule 11 is what caught it. Re-adding stable35 by hand fixes today
# and leaves the next bump to be remembered in two files; deriving deletes
# the second file.
#
# WHAT THE OVERRIDE WAS PROTECTING, AND WHY IT NO LONGER HAS TO.
# Its ORDER carried meaning: E2E (Playwright), Newman and journeydoc-capture
# each ran against `fromJSON(nextcloud-test-refs)[0]`, so stable34 had to
# lead to keep them off a server OpenRegister cannot load — OpenRegister's
# lib/ContextChat/ContentProvider.php implements
# `OCP\ContextChat\IContentProvider`, which does not exist before stable32,
# and on stable31 every `occ` invocation printed
# `Interface "OCP\ContextChat\IContentProvider" not found` while loading
# commands from openregister's info.xml.
#
# The earlier reorder fixed the FIRST-entry problem for E2E/Newman/Journeydoc
# but left "stable31 is still covered by the PHPUnit matrix" — and that leg
# is broken by the same fact. The phpunit job ALSO installs `additional-apps`
# (shared quality.yml, "Checkout additional apps" + "Enabling app: …"), and
# its `occ app:enable openregister` failure is only a ::warning::, so the
# stable31 leg ran on without OpenRegister loaded. openregister has since
# made the floor explicit — `<nextcloud min-version="32"/>`, 8d5181f7a — so
# NC31 is now a configuration this fixture cannot produce at all.
# Those four jobs no longer read a meaning off a position. The shared
# workflow computes a `single-server` output — the numerically HIGHEST
# stable branch in the resolved set, and for the derived path the matrix
# action's own `branches-max` — and they consume that. The action's
# `branches` output is OLDEST-first, so a positional read would have moved
# all four onto stable32 silently. List order is now inert, and the one
# thing the override bought is structural instead.
#
# THIS ADDS A stable35 LEG. It was RED BEFORE IT WAS GREEN, deliberately.
# `additional-apps` below installs openregister and integriq, and when this
# branch was opened (2026-09-15) BOTH still declared `max-version="34"` on
# `development`. The shared workflow aborts the job when `occ app:enable`
# fails for an additional app — that was once only a ::warning::, which is
# how a leg previously ran on WITHOUT OpenRegister loaded and reported
# nothing — so the stable35 legs failed at the fixture, not in the tests.
#
# Removing stable31 corrects an impossible configuration; it does not reduce
# coverage, because nothing was being covered on that leg.
# RESOLVED 2026-09-24: both dependencies now declare
# `<nextcloud min-version="32" max-version="35"/>` on `development`, which
# is the ref this file pins. Verified by parsing each appinfo/info.xml
# rather than grepping, because both files carry comments that quote OTHER
# apps' ranges and a grep matches those first.
#
# THE LIST IS THE WHOLE DECLARED RANGE. appinfo/info.xml declares
# <nextcloud min-version="32" max-version="34"/>, so 32, 33 and 34 each get
# a leg. Adopting NC 34 by REPLACING the list left 32 and 33 advertised to
# the App Store with no job touching them — the declared floor became the
# untested end, which is the same drift as never testing 34, reversed.
# stable34 leads because newman, playwright and journeydoc-capture all read
# `fromJSON(inputs.nextcloud-test-refs)[0]` as their single server.
nextcloud-test-refs: '["stable34", "stable32", "stable33"]'
# The point of deriving the matrix from info.xml stands: the red was the
# honest state of NC 35 support while the gap existed, and nothing here
# could hide it. Leave it derived.
enable-psalm: true
enable-phpstan: true
enable-phpmetrics: true
Expand All @@ -172,7 +186,14 @@ jobs:
# which reads as an auth problem rather than a typo. Six fleet repos hit
# this; in pipelinq it killed all four PHPUnit legs and the E2E job at the
# clone step, so those gates had never executed a single test.
additional-apps: '[{"repo":"ConductionNL/openregister","app":"openregister","ref":"development"}]'
#
# integriq is here because the Integrations page reads integriq's
# `app_connection` rows (adopt-connection-registry). Without it the page
# shows the missing-dependency screen and
# `tests/e2e/workflows/integrations-page.spec.ts` fails on every run.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Concern — the development → beta promotion runs no CI, so the beta release is cut before Playwright has seen the promoted tree

Risk category: untested code reaching a release channel. Why E2E Tests (Playwright), Hydra Gates (axe), Coverage Baseline and app:check-code were skipped on 5b729df:

Job Why skipped Intended?
E2E (Playwright) push to development: shared quality.yml e2e-promotion-only defaults to true (2026-09-13), so push/PR traffic on development is "NOT RUN (declared)"; the PR-event run of this promotion PR never started, because this file's job-level if: (github.event_name != 'pull_request' || github.head_ref != 'development') (line 115, bc82eb4, 2026-09-03) skips every development → beta PR No — the two rules compose into a gap
Hydra Gates (axe) enable-axe not set (default false) and needs: playwright yes (opt-in)
Coverage Baseline if: github.event_name == 'pull_request', and the PR run is skipped by line 115 gap, same cause
app:check-code enable-check-code default false — occ app:check-code no longer exists yes

The shared workflow moved E2E onto "the promotion path" (PRs into beta/main) one week after line 115 started skipping exactly that PR, so for keepiq the promotion into beta is the one trigger that runs nothing. release.yml publishes on push to beta: its beta job ran at 2026-09-29T15:12:52Z on the merge commit 18f594c. The newest E2E evidence at that moment was the nightly dispatch run on fb90ffe, 33 commits behind 5b729df. E2E on the identical tree came only incidentally, from the beta → main PR (run 36589974816: 82 passed, 2 skipped, the three integrations-page tests green), finishing at 15:58 — after the beta artifact was out. This time it was green; nothing structural made it so.

The comment added at line 190–193 ("Without it … integrations-page.spec.ts fails on every run") reads as if E2E runs on every run; on development it runs only on the nightly dispatch.

Impact: any regression only Playwright catches (the vault-unlock, compromise-recovery and integrations flows this release touched) reaches the App Store beta channel before any check could fail on it.

Suggested fix: let the promotion PR run the heavy tier — narrow line 115 to skip only the duplicate fast tier, or drop it now that e2e-promotion-only already keeps E2E off ordinary development traffic — or gate release.yml's beta job on a green E2E for the same SHA. Reword lines 190–193 to say when the spec runs.

Done when:

  • the next development → beta PR shows a non-skipped quality / E2E Tests (Playwright) check (gh pr checks <N> -R ConductionNL/keepiq | grep E2E)
  • or release.yml's beta job has a needs/condition on a green E2E run for its SHA

Introduced by composition: keepiq bc82eb4 (pre-existing, 2026-09-03) + ConductionNL/.github e2e-promotion-only default (2026-09-13); misleading comment from #706 · finding s2-f2 · review of the #691 release promotion at 5b729df

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked as #882 — #691 is already merged, so this is filed against development rather than fixed here.

# `app` is `integriq`, verified in its appinfo/info.xml on `development`
# on 2026-09-15.
additional-apps: '[{"repo":"ConductionNL/openregister","app":"openregister","ref":"development"},{"repo":"ConductionNL/integriq","app":"integriq","ref":"development"}]'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Concern — integriq@development now executes inside CI jobs that hold a write-scoped token

This line adds integriq at the mutable development ref to additional-apps. The caller grants contents: write, actions: write, issues: write, pull-requests: write (:105-109), and in the shared quality.yml the phpunit / newman / playwright jobs that install additional-apps declare no job-level permissions, check out with persist-credentials left at its default, and then occ app:enable integriq runs integriq's PHP (registration, migrations, repair steps) on the same runner. No secrets: are passed — that part is fine.

Impact: anything merged to integriq's development runs in keepiq's CI on every push, with a token that can push to keepiq, dispatch workflows and edit PRs. The same exposure already existed for openregister@development; this doubles the surface.

Suggested fix: in the shared workflow, give the jobs that install third-party apps permissions: contents: read and persist-credentials: false; or pin additional-apps refs to a SHA/tag.

Done when:

  • the shared workflow's phpunit/newman/playwright jobs declare permissions: contents: read (or the checkout sets persist-credentials: false), checked with git -C ~/.github grep -n "persist-credentials\|^ permissions" -- .github/workflows/quality.yml

Found by the persistence audit; the shared-workflow half was read from a local ConductionNL/.github clone that may lag main. The fix lives in ConductionNL/.github, not here.

Introduced by #706 · finding o-f5 · review of the #691 release promotion at 5b729df

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tracked as #881 — #691 is already merged, so this is filed against development rather than fixed here.

enable-sbom: true

# ── Licensing ────────────────────────────────────────────────────────
Expand Down Expand Up @@ -234,9 +255,8 @@ jobs:
# formatter failure mode that made the old `.prettierrc` worth deleting.
# Centralising the config never stopped drift; the gate does.
# Measured on this tree before enabling: PASSES, 294 of 310 tracked
# frontend files in scope. This repo has TWO documentation trees and both
# are excluded by .prettierignore — `docs/` and the separate `docusaurus/`
# site, which has its own package.json and its own toolchain.
# frontend files in scope. The documentation site in `docs/` is excluded
# by .prettierignore: it has its own package.json and its own toolchain.
# `check:l10n-js` regenerates l10n/<locale>.js from the JSON catalogue and
# fails when the committed file is stale. Nextcloud serves ONLY the JS half
# to a browser — raw JSON out of an app directory is a 404 — so a catalogue
Expand Down
8 changes: 3 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
.phpunit.cache/

/node_modules/
/website/node_modules/
/website/.docusaurus/
/js/
# Host-side apps dir bind-mounted by docker-compose.yml; App Store installs
# land here. Only the placeholder that keeps the directory is tracked.
Expand Down Expand Up @@ -87,9 +85,9 @@ docker/dolphin/models/
!issues/
!issues/**

/docusaurus/node_modules/
/docusaurus/build/
/docusaurus/.docusaurus/
# Docusaurus writes its build cache here, with absolute paths from the machine
# that built it. It was committed once; never again.
.docusaurus/
# Test screenshots — images generated by browser test commands (test-app, run-test-scenario)
# Only images are ignored; markdown reports and scenario files are kept in git.
test-results/**/*.png
Expand Down
6 changes: 2 additions & 4 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@ coverage-vitest/
playwright-report/
test-results/
*.min.*
# This repo carries TWO Docusaurus trees, `docs/` and `docusaurus/`. Both are
# documentation sites with their own conventions (their CSS is space-indented
# by Docusaurus' own scaffolding), and both are built by a separate toolchain.
# `docs/` is the Docusaurus documentation site. It has its own conventions (its
# CSS is space-indented by Docusaurus' own scaffolding) and its own toolchain.
docs/
docusaurus/
# Written by the translation workflow — a formatter here would fight its own
# generator on every run.
l10n/
8 changes: 4 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,12 @@ Each release automatically:

## Documentation Release Process

Documentation is built with [Docusaurus](https://docusaurus.io/) and deployed to GitHub Pages.
Documentation is built with [Docusaurus](https://docusaurus.io/) and served by a Cloudflare Worker.

1. Documentation source lives in the `docs/` (or `docusaurus/`) folder on any branch
2. Push or merge to the `documentation` branch triggers the build
1. Documentation source lives in the `docs/` folder
2. A push or merge to `development` triggers the build (`.github/workflows/documentation.yml`)
3. Docusaurus builds the static site
4. The site is deployed to GitHub Pages with a custom domain (e.g., `openregister.app`)
4. The site is published to [keepiq.conduction.nl](https://keepiq.conduction.nl)

Each app has its own documentation site — see the app's README for its URL.

Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,11 +101,10 @@ keepiq/
│ ├── architecture/ # App-specific Architectural Decision Records
│ ├── ROADMAP.md # Product roadmap
│ └── changes/ # OpenSpec change directories (created on first change)
├── docs/ # Design documentation
├── docs/ # Documentation site (Docusaurus) and design documentation
│ ├── ARCHITECTURE.md # Standards, data model, integrations
│ ├── FEATURES.md # Competitive analysis, feature matrix
│ └── DESIGN-REFERENCES.md # Design patterns, ASCII wireframes
├── docusaurus/ # Documentation site
├── tests/ # Unit and integration tests
├── l10n/ # Translations — 36 locales, <locale>.json + generated <locale>.js
├── .github/workflows/ # CI/CD pipelines
Expand Down
24 changes: 13 additions & 11 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Vrij en open source onder de EUPL-1.2-licentie.

**Ondersteuning:** Voor ondersteuning, neem contact op via [email protected].
]]></description>
<version>0.3.2-unstable.20260910105221</version>
<version>0.3.4-unstable.20260912202807</version>
<licence>EUPL-1.2</licence>
<author mail="[email protected]" homepage="https://www.conduction.nl/">Conduction</author>
<namespace>Keepiq</namespace>
Expand Down Expand Up @@ -94,17 +94,19 @@ Vrij en open source onder de EUPL-1.2-licentie.
<!--
Floor is 32, fleet-wide, so PHP 8.3 is guaranteed by the platform.

31 was never actually deliverable: the CI matrix already tests only
stable32 (see nextcloud-test-refs in .github/workflows/
code-quality.yml), because OpenRegister — which this app builds on —
declares `<nextcloud min-version="32" max-version="34"/>` itself. Advertising 31 in
the App Store promised a range no test leg covered and no dependency
could satisfy.

max-version stays 34, which is the fleet-wide value everywhere
except openconnector (35).
31 was never actually deliverable: the CI matrix is derived from
this `<nextcloud>` range (no leg below 32), because OpenRegister —
which this app builds on — declares `min-version="32"` itself.
Advertising 31 in the App Store promised a range no test leg covered
and no dependency could satisfy.

max-version is 35 as of 2026-09-15: verified against Nextcloud 35
(35.0.0 dev) on the shared dev instance — keepiq enabled, its DB
migrations ran cleanly under `occ upgrade`, and the log showed no
deprecations or errors. Dependencies openregister and integriq also
declare 35 on development (verified 2026-09-24).
-->
<nextcloud min-version="32" max-version="34"/>
<nextcloud min-version="32" max-version="35"/>
</dependencies>

<background-jobs>
Expand Down
7 changes: 7 additions & 0 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,10 @@
['name' => 'encryptionSuite#create', 'url' => '/api/v1/suites', 'verb' => 'POST'],
['name' => 'encryptionSuite#updatePrivateKey', 'url' => '/api/v1/suites/{id}/private-key', 'verb' => 'PUT'],
['name' => 'encryptionSuite#revoke', 'url' => '/api/v1/suites/{id}/revoke', 'verb' => 'POST'],
['name' => 'encryptionSuite#forceRevoke', 'url' => '/api/v1/suites/{id}/force-revoke', 'verb' => 'POST'],
['name' => 'encryptionSuite#reinstate', 'url' => '/api/v1/suites/{id}/reinstate', 'verb' => 'POST'],
['name' => 'encryptionSuite#compromiseRecovery','url' => '/api/v1/suites/compromise-recovery', 'verb' => 'POST'],
['name' => 'encryptionSuite#proofChallenge', 'url' => '/api/v1/suites/{id}/proof-challenge', 'verb' => 'GET'],

// CA management (admin-only).
['name' => 'cACertificate#getStatus', 'url' => '/api/v1/ca/status', 'verb' => 'GET'],
Expand All @@ -49,6 +51,7 @@
// Migration tracking.
['name' => 'migration#getStatus', 'url' => '/api/v1/migrations/status', 'verb' => 'GET'],
['name' => 'migration#complete', 'url' => '/api/v1/migrations/{id}/complete', 'verb' => 'POST'],
['name' => 'migration#abort', 'url' => '/api/v1/migrations/{id}/abort', 'verb' => 'POST'],

// Compromise-recovery migration work loop. One record per request: the
// browser decrypts with the old private key, re-encrypts under the new one,
Expand All @@ -59,6 +62,10 @@
['name' => 'migration#reEncryptSecret', 'url' => '/api/v1/migrations/{id}/secrets/{secretId}', 'verb' => 'POST'],
['name' => 'migration#reEncryptVersion', 'url' => '/api/v1/migrations/{id}/versions/{versionId}', 'verb' => 'POST'],
['name' => 'migration#reEncryptAttachmentGrant', 'url' => '/api/v1/migrations/{id}/attachment-grants/{grantId}', 'verb' => 'POST'],
// Emergency contacts migrate too, but off the gate: the browser mints a fresh
// envelope escrowing the new key and re-points the contact here. A contact it
// cannot carry is left for the completion sweep to invalidate.
['name' => 'migration#reEnvelopeEmergencyContact', 'url' => '/api/v1/migrations/{id}/emergency-contacts/{contactId}', 'verb' => 'POST'],

// Key generator endpoint (stateless, authenticated).
['name' => 'keyGenerator#generate', 'url' => '/api/v1/generate-key', 'verb' => 'POST'],
Expand Down
9 changes: 5 additions & 4 deletions cli/ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,17 @@ func ciSetup() (c *client.Client, key *rsa.PrivateKey, disc *client.Discovery, b
}

// fetchDecrypt fetches an application secret by name and decrypts its envelope
// with the application private key (§4.2). Returns the plaintext value.
// with the application private key (§4.2). Returns the plaintext value, which
// the server sends as `ciphertext.key` under the scheme in `encryption.scheme`.
func fetchDecrypt(c *client.Client, key *rsa.PrivateKey, name, bearer string) (string, error) {
env, err := c.FetchByName(name, bearer)
if err != nil {
return "", err
}
if env.Scheme != "rsa-oaep-sha256-chunked-v1" {
return "", fmt.Errorf("unexpected envelope scheme %q", env.Scheme)
if env.Encryption.Scheme != "rsa-oaep-sha256-chunked-v1" {
return "", fmt.Errorf("unexpected envelope scheme %q", env.Encryption.Scheme)
}
return dcrypto.DecryptField(env.Payload.Value, key)
return dcrypto.DecryptField(env.Ciphertext.Key, key)
}

func cmdCIFetch(args []string) error {
Expand Down
Loading
Loading