Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
321 commits
Select commit Hold shift + click to select a range
3cd2f4f
fix(demo): neutral "Redirecting..." copy on email sign-in button
aspiers Apr 20, 2026
07d3817
fix(pds-core): chooser enrichment for live OAuth flow
aspiers Apr 20, 2026
d02ff54
ci(e2e): check out PR head SHA, not merge commit
aspiers Apr 20, 2026
efa9c49
test(e2e): implement account-recovery feature scenarios
aspiers Apr 20, 2026
f72d458
test(e2e): reset browser before capturing page in verified-backup-ema…
aspiers Apr 20, 2026
33b8b81
test(e2e): silence Sonar hotspots in account-recovery steps
aspiers Apr 20, 2026
260113b
fix(auth-service): complete recovery flow for backup-email sessions
aspiers Apr 20, 2026
f56365f
refactor(auth-service): extract resolveRecoveryEmail helper + unit tests
aspiers Apr 20, 2026
eb88c7f
Merge pull request #93 from hypercerts-org/feat/email-preview-endpoint
aspiers Apr 20, 2026
67b4bcb
Merge pull request #98 from hypercerts-org/hyper-account-recovery
aspiers Apr 20, 2026
1bf9ce1
docs: scrub internal ticket IDs from consumer-facing artifacts
aspiers Apr 20, 2026
7229c22
Merge pull request #96 from hypercerts-org/fix/oauth-session-reuse
aspiers Apr 20, 2026
a03ad58
docs(pds-white-boxing): catalogue cross-client session-reuse debt
aspiers Apr 20, 2026
70fb816
Merge pull request #101 from hypercerts-org/docs/pds-white-boxing-ses…
aspiers Apr 20, 2026
548f4ad
feat(auth-service): redirect / to /account
aspiers Apr 20, 2026
1506fbe
Migrate workflows to Blacksmith
blacksmith-sh[bot] Apr 21, 2026
d269604
ci: trigger CI on merge_group events
aspiers Apr 21, 2026
a533794
Merge pull request #105 from hypercerts-org/blacksmith-migration-70fb816
aspiers Apr 21, 2026
cbb53d9
Merge pull request #106 from hypercerts-org/ci-merge-group-trigger
aspiers Apr 21, 2026
1128699
Merge pull request #102 from hypercerts-org/root-redirect-to-account
aspiers Apr 21, 2026
02cc872
feat(auth-service): style all error pages with shared renderError helper
s-adamantine Apr 18, 2026
4840690
fix(auth-service): delegate to Express default handler when headers sent
s-adamantine Apr 20, 2026
eb88be8
style: apply prettier formatting
s-adamantine Apr 20, 2026
1f4108c
fix(auth-service): split next(err) from return to satisfy lint rule
s-adamantine Apr 20, 2026
541cf88
fix(auth-service): prefer JSON when negotiating, set explicit html type
s-adamantine Apr 20, 2026
f76a771
chore: add changeset for auth-service error page styling
s-adamantine Apr 20, 2026
9c3e7e9
test(auth-service): cover renderError helper and error-handler middle…
aspiers Apr 20, 2026
2228e4d
refactor(auth-service): extract 404/500 middleware to a lib module
aspiers Apr 20, 2026
7e47463
style(auth-service): reorder error-handlers.test imports per repo rules
aspiers Apr 22, 2026
4b38770
fix(auth-service): add Vary: Accept to 404/500 handlers
aspiers Apr 22, 2026
a8890c8
chore: ratchet coverage thresholds after error-page tests
aspiers Apr 22, 2026
9130fca
chore: lower ratcheted thresholds to CI-observed floors
aspiers Apr 22, 2026
ee9736c
Merge pull request #97 from hypercerts-org/sharfy/style-auth-error-pages
aspiers Apr 22, 2026
d48f735
feat(auth-service): add certified favicon to rendered pages
s-adamantine Apr 16, 2026
9ccfa25
feat(auth-service): wire favicon into remaining recovery/error pages
s-adamantine Apr 16, 2026
d0c9748
test(auth-service): assert favicon link in every rendered <head>
aspiers Apr 20, 2026
bb6e7fc
test(auth-service): auto-discover route files for favicon coverage
aspiers Apr 21, 2026
4a872fa
style(auth-service): reorder favicon.test imports per repo convention
aspiers Apr 21, 2026
0fceb54
feat(auth-service): add dark-mode favicon variant
aspiers Apr 21, 2026
cfa02ce
feat(pds-core): wire favicon into rendered error + consent pages
aspiers Apr 21, 2026
63f91f9
refactor(pds-core): factor /static mount into testable helper
aspiers Apr 21, 2026
f1a9865
style(pds-core): apply prettier formatting to static-mount files
aspiers Apr 21, 2026
b3864c9
feat(auth-service,pds-core): alias /favicon.ico to light-theme SVG
aspiers Apr 21, 2026
6670eba
refactor(shared): hoist mountStaticAssets helper into @certified-app/…
aspiers Apr 22, 2026
b993898
fix(docker): ship pds-core public/ dir into the runtime image
aspiers Apr 22, 2026
727db89
chore: exclude .claude/worktrees from lint + format
aspiers Apr 22, 2026
db17a3a
feat(pds-core): inject favicon into upstream-rendered HTML
aspiers Apr 22, 2026
f4639aa
test(e2e): add favicon scenarios covering both services
aspiers Apr 22, 2026
545ae40
fix(auth-service): add favicon to shared renderError after PR 97 extr…
aspiers Apr 22, 2026
50226e6
test(e2e): fix Gherkin parse error in favicon.feature description
aspiers Apr 23, 2026
eafbc86
test(e2e): fix auth-service login page path in favicon scenario
s-adamantine Apr 24, 2026
efe975b
feat(auth-service,pds-core): replace dark favicon with certified bran…
s-adamantine Apr 24, 2026
ff9e328
refactor: inline static-mount helper, drop express from @certified-ap…
s-adamantine Apr 24, 2026
c74f889
ci(e2e): seed empty junit stub so reporter doesn't fail on early aborts
s-adamantine Apr 24, 2026
b89dbd1
Merge pull request #85 from hypercerts-org/sharfy/hyper-327-add-certi…
s-adamantine Apr 24, 2026
63049ce
docs(session-reuse): design + feature for post-#96 stale-cookie failures
aspiers Apr 21, 2026
3505340
feat(pds-core): add welcome-page guard to eliminate stock upstream si…
aspiers Apr 21, 2026
01f9f3b
test(session-reuse): add E2E coverage for welcome-page guard
aspiers Apr 21, 2026
6e98895
feat(pds-core): hide chooser handle in random mode via meta tag
aspiers Apr 21, 2026
6456c1e
fix(auth-service): require full dev-id+ses-id pair and clear orphans
aspiers Apr 21, 2026
f794f2f
fix(pds-core): rebind upstream "Another account" + hide "Sign up" on …
aspiers Apr 21, 2026
888149b
test(session-reuse): add E2E coverage for random-mode handle hiding
aspiers Apr 21, 2026
a983fe3
fix(session-reuse): address PR #103 review comments
aspiers Apr 21, 2026
65ef1e8
fix(session-reuse): address second round of PR #103 review comments
aspiers Apr 21, 2026
6c0ad14
fix(pds-core): await client-metadata lookup in chooser enrichment
aspiers Apr 22, 2026
b716110
test(pds-core): deduplicate chooser middleware test harness
aspiers Apr 22, 2026
3ddcc21
fix(session-reuse): address fourth round of PR #103 review comments
aspiers Apr 22, 2026
7ba867f
feat(preview): two-column layout for the auth-service preview list
aspiers Apr 26, 2026
226781b
feat(pds-core): add /preview/chooser route with bound query controls
aspiers Apr 26, 2026
7ebce7e
fix(preview): lay each route row out as a flex line so controls stay …
aspiers Apr 26, 2026
e812648
fix(pds-core): validate ses-id against device row in welcome-page guard
aspiers Apr 26, 2026
9507688
chore: address SonarCloud findings on PR #103
aspiers Apr 27, 2026
be513b5
fix(auth-service): honour EPDS_ALLOW_PRIVATE_IPS for email-template f…
aspiers Apr 27, 2026
21eec6f
fix(auth-service): add EPDS_DISABLE_RATE_LIMIT bypass for per-IP rate…
aspiers Apr 27, 2026
dcd58af
test(session-reuse): align scenarios with upstream chooser-on-every-r…
aspiers Apr 27, 2026
164f5a3
test(session-reuse): sketch @pending scenarios for trusted-client cho…
aspiers Apr 27, 2026
210b986
fix(session-reuse): wire untrusted demo as confidential OAuth client
aspiers Apr 27, 2026
fd5d629
refactor(pds-core): extract preview-shared to fix Sonar duplication gate
aspiers Apr 27, 2026
9b608ff
docs(session-reuse): add coverage matrix + drop A/B/C/D scenario lett…
aspiers Apr 27, 2026
0762465
docs(changeset): merge session-reuse-robustness into cross-client-ses…
aspiers Apr 27, 2026
7de12b8
docs(changeset,skill): trim cross-client changeset + codify depth/str…
aspiers Apr 27, 2026
0a65e05
docs(changeset): drop hallucinated login_hint claims + out-of-scope r…
aspiers Apr 27, 2026
3ccb48d
docs(changeset): add operator changeset for EPDS_DISABLE_RATE_LIMIT
aspiers Apr 27, 2026
0e676c7
docs(changeset): explain how to actually set prompt=login
aspiers Apr 27, 2026
5ec70db
feat(demo): add prompt=login checkbox to exercise ePDS forced re-auth
aspiers Apr 27, 2026
3c6d2c4
docs(skill): document prompt=login URL-vs-PAR-body trap in epds-login
aspiers Apr 27, 2026
aa6bb06
docs(changeset): clarify prompt=login goes on the URL, not PAR body
aspiers Apr 27, 2026
bcfb1ff
Merge pull request #103 from hypercerts-org/session-reuse-bugs
aspiers Apr 27, 2026
e1d49dc
docs(skill): tighten prompt=login section
aspiers Apr 27, 2026
0d9a315
Merge pull request #112 from hypercerts-org/docs/skill-prompt-login-c…
aspiers Apr 27, 2026
26c317b
fix(preview): clamp numAccounts to [1, 10] on chooser preview route
aspiers Apr 27, 2026
baae9a2
Merge pull request #113 from hypercerts-org/fix/preview-chooser-empty…
aspiers Apr 27, 2026
527e8c5
feat(session-reuse): gate Flow 1 chooser on login_hint matching devic…
aspiers Apr 27, 2026
ee96475
chore(sonar): suppress http URL hotspot in fetch-device-accounts test
aspiers Apr 27, 2026
b3f7715
chore: drop sdsls.dev mentions from third-party-app comments
aspiers Apr 27, 2026
8b1dfc1
fix(session-reuse): address review feedback on the Flow 1 hint gate PR
aspiers Apr 27, 2026
f3fdc00
fix(session-reuse): re-normalise binding emails to lowercase at compa…
aspiers Apr 27, 2026
1b14351
fix(session-reuse): more review-feedback cleanup
aspiers Apr 27, 2026
539c0de
fix(session-reuse): rename "OTP form is pre-filled" step
aspiers Apr 27, 2026
e3c7ddb
refactor(demo): drop unused per-IP rate limiter on /api/oauth/login
aspiers Apr 27, 2026
e0261a9
refactor(welcome-page-guard): use shared loadDeviceAccountEmails helper
aspiers Apr 27, 2026
d608f6d
Merge pull request #114 from hypercerts-org/flow1-hint-skip-chooser
aspiers Apr 27, 2026
7f265b7
feat(auth): add ATProto/Bluesky handle login button to login page
aspiers Apr 27, 2026
9ba64d5
fix(auth): make ATProto button a real <button>; drop ambiguous step; …
aspiers Apr 28, 2026
9d3017c
fix(e2e): drive scenarios via demo flow2 to land on auth-service logi…
aspiers Apr 28, 2026
55097d8
docs(tutorial): document epds_handle_login_url client-metadata field
aspiers Apr 28, 2026
0bae795
fix(e2e): use direct .btn-atproto selector + waitForLoadState for the…
aspiers Apr 28, 2026
44cdafe
fix(e2e): normalize demoUrl trailing slash consistently
aspiers Apr 28, 2026
546c4b2
docs(skill): document epds_handle_login_url in client-metadata reference
aspiers Apr 28, 2026
353d0a3
Merge pull request #115 from hypercerts-org/feat/atproto-login-button
s-adamantine Apr 28, 2026
21a8bef
feat(auth-service): allow trusted clients to inject custom favicon
s-adamantine Apr 16, 2026
b8dee5e
test(session-reuse): reproduce host-only cookie shadowing loop (#116)
aspiers Apr 28, 2026
e11b71a
fix(pds-core): preserve host-only Set-Cookie clears in cookie-domain …
aspiers Apr 28, 2026
b674db9
fix(pds-core): evict host-only twin cookies on /oauth/epds-callback
aspiers Apr 28, 2026
94f406d
ci(e2e): bump Railway deploy poll timeout from 3 to 6 minutes
aspiers Apr 28, 2026
a7d482e
Merge pull request #118 from hypercerts-org/ci/e2e-deploy-timeout-6min
aspiers Apr 28, 2026
03870dc
fix(pds-core): gate host-only cookie clears on cookie-domain broadening
aspiers Apr 28, 2026
820af82
test(session-reuse): reuse existing email/OTP steps in host-only scen…
aspiers Apr 28, 2026
9a1dcda
test(session-reuse): merge host-only shadowing scenario into session-…
aspiers Apr 28, 2026
3f5d5c1
Merge pull request #117 from hypercerts-org/fix/host-only-cookie-shadow
aspiers Apr 28, 2026
92b9a40
Merge pull request #86 from hypercerts-org/sharfy/favicon-injection
s-adamantine Apr 28, 2026
77d136f
feat(auth-service): restyle login page + ATProto handle sign-in toggle
s-adamantine Apr 26, 2026
5fc9721
test(e2e): drive segmented OTP boxes instead of hidden #code input
s-adamantine Apr 27, 2026
f4f1040
feat(auth-service): recovery link + e2e fixes after restyle
s-adamantine Apr 27, 2026
b83a387
fix(auth-service): address PR #110 review feedback
aspiers Apr 28, 2026
0e5bf82
fix(e2e): expect "Enter your email address" label after restyle
aspiers Apr 28, 2026
57f77d1
feat(auth-service): drive ToS/privacy links from PDS env vars
aspiers Apr 28, 2026
dde3bdf
feat(pds-core): inject default Certified branding CSS
aspiers Apr 28, 2026
dd5faf4
docs(preview): note ATProto-button gating uses real client metadata
aspiers Apr 29, 2026
c9a18d0
Merge pull request #110 from hypercerts-org/restyle-login-page
s-adamantine Apr 29, 2026
323ab09
fix(changesets): use correct package name 'ePDS' (not 'epds')
aspiers Apr 29, 2026
f0d817a
Merge pull request #120 from hypercerts-org/fix/changeset-package-nam…
aspiers Apr 29, 2026
0488cc7
fix(changesets): add **Affects:** line to two changesets
aspiers Apr 29, 2026
d8871bf
chore(changesets): drop demo-neutral-submitting-copy
aspiers Apr 29, 2026
b98436c
docs(changeset): trim post-flush crash entry to operator-relevant detail
aspiers Apr 29, 2026
7ae8a8c
Merge pull request #121 from hypercerts-org/fix/changeset-missing-aff…
aspiers Apr 29, 2026
ada7089
chore: release
github-actions[bot] Apr 29, 2026
5f9ce81
docs(changelog): apply edits from restyle-login-page PR
s-adamantine Apr 29, 2026
b2bad96
docs(changelog): restore literal #999 in CSS code fence
s-adamantine Apr 29, 2026
b8a5296
Merge pull request #123 from hypercerts-org/changeset-release/main
s-adamantine Apr 29, 2026
dacf1d2
fix(auth-service): keep auth_flow alive past OTP expiry, plus e2e
aspiers Apr 29, 2026
6e6c3dc
Merge pull request #122 from hypercerts-org/test/otp-expiry-e2e
aspiers Apr 30, 2026
8bf888b
fix(login-page): open Terms and Privacy links in a new tab
s-adamantine Apr 30, 2026
9ab9cde
style(login-page): explicit cursor:pointer on legal links and logo
s-adamantine Apr 30, 2026
c86e237
test(e2e): reproduce sign-in-view leaks past welcome-page guard
aspiers Apr 30, 2026
f5baf3c
test(pds-core): add expire-device-account hook for sign-in-view repros
aspiers Apr 30, 2026
6a8671d
feat(auth-service): show "Powered by Certified" footer on /account/lo…
s-adamantine Apr 30, 2026
0f2b19d
refactor(pds-core): reuse PDS Kysely handle in expire-device-account …
aspiers Apr 30, 2026
ee7813a
fix(pds-core): bounce sign-in-view leaks past the welcome-page guard
aspiers Apr 30, 2026
7eb6e59
test(e2e): row 9 needs the login_hint in the PAR body, not the URL
aspiers Apr 30, 2026
5961183
refactor: address PR #129 review feedback
aspiers Apr 30, 2026
3ac1589
feat(auth-service): show "Powered by Certified" footer on handle pick…
s-adamantine Apr 30, 2026
489e3fc
feat(auth-service): show "Powered by Certified" footer on settings, d…
s-adamantine Apr 30, 2026
ea56f77
Merge pull request #127 from hypercerts-org/fix/login-legal-links-new…
aspiers Apr 30, 2026
13019a6
test(pds-core): cover sign-in-view bounce branches + extract shared d…
aspiers Apr 30, 2026
236dd39
test(pds-core): factor out test scaffolding to drop Sonar duplication
aspiers Apr 30, 2026
c68fe8a
chore(changeset): collapse powered-by footer changesets into one
s-adamantine Apr 30, 2026
ac6b8c7
refactor(pds-core): rename welcome-page-guard → auth-ui-guard
aspiers Apr 30, 2026
215f4d8
Merge pull request #130 from hypercerts-org/fix/account-login-powered…
s-adamantine Apr 30, 2026
3e57080
fix(pds-core): strip prompt=login from stored PAR after a successful …
aspiers Apr 30, 2026
14e5033
fix(pds-core): tokenise prompt, opt-out test hooks by default, drop p…
aspiers Apr 30, 2026
bdddfaf
Merge pull request #129 from hypercerts-org/welcome-guard-inadequate
aspiers Apr 30, 2026
bce65b5
fix(auth-service): drop duplicate OTP verify submits that flashed "In…
s-adamantine Apr 30, 2026
3d659a2
test(auth-service): hoist renderDefault helper to module scope
aspiers Apr 30, 2026
ef55606
test(auth-service): replace ReDoS-flagged regex with substring count
aspiers Apr 30, 2026
56c6665
fix(auth-service): clear OTP boxes on verify error
aspiers May 1, 2026
e6745e7
fix(auth-service): centre flash messages and add stable CSS hook
aspiers May 1, 2026
e232aa0
docs(changeset): tighten OTP changeset and reaudience CSS hook
aspiers May 1, 2026
9392adc
docs(changeset): rewrite per writing-changesets skill
aspiers May 1, 2026
b8e4349
Merge pull request #134 from hypercerts-org/fix/otp-double-submit-flash
aspiers May 1, 2026
143ff35
fix: mobile consent action layout
Kzoeps May 1, 2026
20ad5e6
Merge pull request #136 from hypercerts-org/signin-with-certified-but…
Kzoeps May 1, 2026
1fc0ac1
test(e2e): repro raw-JSON leak when PAR has expired
aspiers Apr 30, 2026
0e62bd6
fix(pds-core): friendly OAuth redirect when PAR has expired
aspiers Apr 30, 2026
369a557
refactor(shared): extract postHook test helper into the shared package
aspiers May 1, 2026
16d5de6
refactor(shared): extract styled renderError into the shared package
aspiers May 1, 2026
a8cd3bc
Merge pull request #128 from hypercerts-org/fix/par-expiry-existing-u…
aspiers May 1, 2026
899346c
fix(auth-service): land on email form when prompt=login overrides log…
aspiers May 1, 2026
404063c
fix(auth-service): short-circuit login_hint resolution on prompt=login
aspiers May 1, 2026
28c3a0d
test(auth-service): cover prompt=login branch of GET /oauth/authorize
aspiers May 1, 2026
6f4a65d
test(auth-service): silence sonar hotspots in prompt-login route test
aspiers May 1, 2026
1ce864e
fix(auth-service): honour space-delimited and array prompt values
aspiers May 1, 2026
83e8012
test(auth-service): close the dual-handle SQLite leak in prompt=login…
aspiers May 1, 2026
dfee0f8
fix(pds-core,auth-service): use epds_skip_par_hint signal for "Anothe…
aspiers May 1, 2026
15814d7
test(e2e): retry PDS /health on the env-up Given against transient blips
aspiers May 1, 2026
592a7a6
fix(auth-service,e2e): clear cleanup interval on context destroy + bo…
aspiers May 1, 2026
84f7a93
Merge pull request #141 from hypercerts-org/fix/another-account-skips…
aspiers May 1, 2026
45d7181
docs(demo): flag EPDS_CLIENT_THEME as required for client-branding e2e
aspiers May 1, 2026
87b5d96
Merge pull request #145 from hypercerts-org/docs/demo-theme-required-…
aspiers May 1, 2026
d7b3893
chore: release
github-actions[bot] May 1, 2026
e678b86
Merge pull request #144 from hypercerts-org/changeset-release/main
aspiers May 1, 2026
69de2e3
docs(demo): tighten EPDS_CLIENT_THEME e2e note per PR #145 review
aspiers May 1, 2026
ffc17bd
Merge pull request #146 from hypercerts-org/docs/demo-theme-note-tighten
aspiers May 1, 2026
ba8f268
test(e2e): repro OAuth dead-ends when PAR dies before user completes …
aspiers May 4, 2026
64c50f7
docs(design): PAR expiry, heartbeats, and clean-exit strategy
aspiers May 4, 2026
b1fc940
feat(auth-service): keep PAR alive while user sits on OTP / recovery …
aspiers May 4, 2026
339aba3
test(e2e): cover @par-heartbeat liveness end-to-end
aspiers May 4, 2026
2e4d327
feat(auth-service,pds-core): redirect to OAuth client on sign-in fail…
aspiers May 4, 2026
5183b7b
docs(design): record heartbeat + clean-exit landed in the design doc
aspiers May 4, 2026
8bcb344
fix(demo): mark /client-metadata.json route dynamic so EPDS_CLIENT_TH…
aspiers May 4, 2026
9fb0e69
fix: address PR #154 review feedback (CodeRabbit + Copilot + SonarQub…
aspiers May 4, 2026
3b0ec50
fix(demo): default EPDS_CLIENT_THEME to amber so client-branding e2e …
aspiers May 4, 2026
b6f3ba9
test(e2e): rename "the user can try again" to "the OTP entry boxes ar…
aspiers May 4, 2026
fcab1c7
fix(auth-service): forward no_heartbeat through recovery Verify form too
aspiers May 4, 2026
f3766a9
test(auth-service,shared): bring new heartbeat / clean-exit code to 1…
aspiers May 4, 2026
241807a
fix: address SonarCloud quality-gate findings on PR #154
aspiers May 4, 2026
25f37aa
test(auth-service): unit-test buildEpdsCallbackUrl + ratchet down fun…
aspiers May 4, 2026
7650f14
fix: address third-round PR #154 review feedback
aspiers May 4, 2026
4bc113d
test(auth-service): replace `q.get(name)!` with a typed helper to cle…
aspiers May 4, 2026
26151cd
refactor(shared): extract resolveStartOverHref to fix Sonar dup-block…
aspiers May 4, 2026
3d31876
fix(auth-service): inline "Send a new code" action on expired-OTP error
aspiers May 4, 2026
03958c5
test(e2e): allow inline action button alongside the OTP error message
aspiers May 4, 2026
86cd60f
fix(auth-service): don't offer Resend when it cannot complete the sig…
aspiers May 4, 2026
160f20d
test(auth-service): extract heartbeat-router test harness + replace R…
aspiers May 4, 2026
72d9113
Merge pull request #154 from hypercerts-org/fix/otp-resend-after-par-…
aspiers May 5, 2026
1e4fb3b
ci(release): auto-cut tag + GitHub Release on Release-PR merge
aspiers May 5, 2026
b008cd4
chore(changeset): downgrade slow-signin fixes from minor to patch
aspiers May 5, 2026
fcdd5ab
Merge pull request #159 from hypercerts-org/release-auto-tag
aspiers May 5, 2026
2551e20
Merge pull request #160 from hypercerts-org/changeset-bumps-to-patch
aspiers May 5, 2026
de69725
ci(e2e): skip the run job on the changesets Release PR
aspiers May 5, 2026
b23f3a2
Merge pull request #161 from hypercerts-org/e2e-skip-release-pr
aspiers May 5, 2026
4469498
chore: release
github-actions[bot] May 5, 2026
08f7d24
Merge pull request #158 from hypercerts-org/changeset-release/main
aspiers May 5, 2026
c2ca73a
test(e2e): cover auth_flow TTL regression boundary
aspiers Apr 30, 2026
e060e2f
Merge pull request #124 from hypercerts-org/test/auth-flow-ttl
aspiers May 6, 2026
e873e7a
fix(pds-core): add email label spacing
Kzoeps May 7, 2026
cb904e9
Merge pull request #166 from hypercerts-org/karma/hyper-386-bug-missi…
aspiers May 7, 2026
cc65707
feat(pds-core): login redirection card alignment
Kzoeps May 13, 2026
f221800
fix(demo): improve amber branding contrast
Kzoeps May 13, 2026
49a0579
Merge pull request #169 from hypercerts-org/karma/hyper-395-align-the…
aspiers May 13, 2026
6faf552
chore: release
github-actions[bot] May 13, 2026
7f3bc8e
Merge pull request #170 from hypercerts-org/changeset-release/main
aspiers May 13, 2026
c1edcf3
ci(linear): report deployments as releases
aspiers May 13, 2026
208cc24
ci(linear): pin Linear release action
aspiers May 13, 2026
a2b238a
ci(linear): restrict release sync to Railway deployments
aspiers May 13, 2026
4d82506
Merge pull request #171 from hypercerts-org/add-linear-release-workflow
aspiers May 13, 2026
c851104
docs(skills): add instatus maintenance scheduling skill
aspiers May 13, 2026
fc501ab
ci(linear): gate staging releases on dev
aspiers May 13, 2026
71408ad
docs(skills): clarify Instatus timestamp examples
aspiers May 13, 2026
b0eb754
Merge pull request #172 from aspiers/add-instatus-maintenance-skill
aspiers May 13, 2026
286b30f
ci: run e2e suite with three Cucumber workers
Kzoeps May 14, 2026
875bd73
fix(e2e): isolate auth flow expiry hook
Kzoeps May 14, 2026
15505ce
Merge pull request #174 from hypercerts-org/ci/e2e-parallel-3
aspiers May 14, 2026
a00cb25
feat(shared): preview validation flags missing epds_handle_login_url
aspiers May 18, 2026
7c807af
docs(shared): align epds_handle_login_url docstring with http(s) gate
aspiers May 18, 2026
ad4e554
docs(changeset): trim preview-validate-handle-login-url to essentials
aspiers May 18, 2026
0c8a3e0
docs(shared): align handle-login-url wording with runtime behavior
aspiers May 19, 2026
dad840a
docs(shared): soften https-in-production wording in epds_handle_login…
aspiers May 19, 2026
7dd6ee8
Merge pull request #176 from hypercerts-org/preview-validate-handle-l…
aspiers May 19, 2026
41e108f
Merge upstream/main (hypercerts-org) into attpslabs/main
daveselfsurf May 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 44 additions & 10 deletions .agents/skills/epds-login/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,18 +184,52 @@ The abbreviated version:
3. Redirect browser to `/oauth/authorize?...&login_hint=<email>`
4. Handle callback: verify state, exchange code for tokens (with DPoP nonce retry)

## Forcing a Fresh Sign-In (`prompt=login`)

When a previous sign-in's cookies are present in the browser, ePDS skips
the email code form and lands the user on the account chooser to confirm
which identity to reuse. To force the email code form instead, use the
standard OIDC `prompt=login` parameter.

**Important — where to put it:** ePDS's auth service decides whether to
engage session reuse by inspecting the **query string** of the
`/oauth/authorize` redirect. PAR-body `prompt=login` is ignored.

If your OAuth library (e.g. `NodeOAuthClient`) only supports passing
`prompt` via the PAR body, you must also append `&prompt=login` to the
authorization URL the library returns before redirecting the user.

**Hand-rolled (Flow 1):**

```typescript
const authUrl =
`${authEndpoint}?client_id=${encodeURIComponent(clientId)}` +
`&request_uri=${encodeURIComponent(parData.request_uri)}` +
(forceLogin ? '&prompt=login' : '')
```

**With `NodeOAuthClient` (Flow 2):**

```typescript
const url = await client.authorize(input, { prompt: 'login' })
// Library puts prompt in PAR; also append it to the URL query string
// so ePDS's session-reuse short-circuit fires.
url.searchParams.set('prompt', 'login')
```

## Common Pitfalls

| Pitfall | Fix |
| ---------------------------------- | --------------------------------------------------------------------------------------------- |
| Consent screen on every login | Switch to `private_key_jwt` — public clients force consent unless in the PDS trusted list |
| Flash of email form (Flow 1) | Include `login_hint` on the **auth redirect URL only** (never in the PAR body) |
| `Invalid login_hint` from PAR | Remove `login_hint` from the PAR body — PDS core only accepts handles/DIDs, not emails |
| `auth_failed` immediately | Check Caddy logs — likely a DNS/upstream name mismatch |
| DPoP rejected (hand-rolled only) | Always implement the nonce retry loop (ePDS always demands a nonce) |
| Token exchange fails (hand-rolled) | Restore the DPoP key pair from the session cookie, don't generate a new one |
| `Cannot find package` in tests | Run `pnpm build` before `pnpm test` — vitest needs `dist/` |
| `NodeOAuthClient` callback 401 | Ensure `stateStore` and `sessionStore` persist across requests (not in-memory for serverless) |
| Pitfall | Fix |
| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| Consent screen on every login | Switch to `private_key_jwt` — public clients force consent unless in the PDS trusted list |
| Flash of email form (Flow 1) | Include `login_hint` on the **auth redirect URL only** (never in the PAR body) |
| `Invalid login_hint` from PAR | Remove `login_hint` from the PAR body — PDS core only accepts handles/DIDs, not emails |
| `auth_failed` immediately | Check Caddy logs — likely a DNS/upstream name mismatch |
| DPoP rejected (hand-rolled only) | Always implement the nonce retry loop (ePDS always demands a nonce) |
| Token exchange fails (hand-rolled) | Restore the DPoP key pair from the session cookie, don't generate a new one |
| `Cannot find package` in tests | Run `pnpm build` before `pnpm test` — vitest needs `dist/` |
| `NodeOAuthClient` callback 401 | Ensure `stateStore` and `sessionStore` persist across requests (not in-memory for serverless) |
| `prompt=login` ignored, chooser still shown | Append `&prompt=login` to the **authorize URL** query string — PAR body alone doesn't engage ePDS's short-circuit |

## Handles

Expand Down
54 changes: 33 additions & 21 deletions .agents/skills/epds-login/references/client-metadata.md

Large diffs are not rendered by default.

241 changes: 241 additions & 0 deletions .agents/skills/scheduling-instatus-maintenance/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
---
name: scheduling-instatus-maintenance
description: Draft and schedule Instatus maintenance windows for ePDS services. Use when users mention maintenance windows, scheduled maintenance, upgrades/releases, reconfiguration, staging/production ePDS, certified.one, dev.certified.app, or Instatus scheduling.
---

# Scheduling Instatus Maintenance

Use this for ePDS planned maintenance on the Certified Instatus page: release
upgrades, configuration changes, email/provider switches, and similar planned work.

## Golden Rules

- **Draft first unless the user explicitly says to schedule it.**
- Use UTC ISO timestamps (`YYYY-MM-DDTHH:mm:ss.000Z`).
- Follow prior wording and field conventions; do not invent a new style.
- Do not expose or print API keys. If using direct HTTP, summarize only the created maintenance ID and key fields.
- Prefer the Instatus MCP tools for reads. For creates, include component status mappings if the wrapper/tool needs them.

## Status Page and Component IDs

Status page:

- **Certified** — page ID `cmmxeox60009t11pyrg02vclw`, public URL `https://certified.instatus.com`

Staging components:

- `dev.certified.app (ePDS)` — `cmmxf6unk000h1655r6yuwcl2`
- `auth.dev.certified.app (Auth service for dev.certified.app ePDS)` — `cmmxfauhw00e0aa5b6pabeg9r`

Production components:

- `certified.one (ePDS)` — `cmmxf4qrx00exuud43e815zo7`
- `auth.certified.one (Auth service for certified.one ePDS)` — `cmmxf9lq5001ae87amwcw4zm2`

## Scheduling Defaults

Default window length is **60 minutes** unless the user says otherwise.

Prior windows commonly used:

- `notify: true`
- `notifyStart: true`
- `notifyEnd: true`
- `notifyEarly: true`
- `notifyMinutes: 30`
- `autoStart: true`
- `autoEnd: true`
- `isCollapsed: true`
- `impact` / component maintenance status: `UNDERMAINTENANCE`
- `status: NOTSTARTEDYET` for future scheduled work

## Title Patterns

Use concise, consistently formatted titles across staging and production. Put
the work type first when it is meaningful, especially `upgrade`.

- Staging upgrade: `upgrade of dev.certified.app (staging ePDS)`
- Production upgrade: `upgrade of certified.one (production ePDS)`
- Staging reconfiguration: `reconfiguration of dev.certified.app (staging ePDS) - <short description>`
- Production reconfiguration: `reconfiguration of certified.one (production ePDS) - <short description>`
- Other maintenance: `<work type> of <service> (<environment> ePDS) - <short description>`

## Message Patterns

Keep messages concise, usually one sentence. Two short sentences are acceptable when the second sentence calls out the expected temporary impact.

Release upgrade:

```text
We are planning for a scheduled maintenance during that time, to upgrade <environment> to <version>, which <briefly states the user-visible/operator-visible reason>.
```

Examples:

```text
We are planning for a scheduled maintenance during that time, to upgrade staging to 0.6.3, which applies a small cosmetic fix for consistent auth-service page branding.
```

```text
We are planning for a scheduled maintenance during that time, to upgrade from 0.6.1 to 0.6.2, which fixes a few bugs.
```

Reconfiguration / provider switch:

```text
We are planning for a scheduled maintenance during that time to <short action>. <Affected capability> may be briefly unavailable during this period.
```

Example:

```text
We are planning for a scheduled maintenance during that time to switch the email configuration. Email-based login may be briefly unavailable during this period.
```

Generic maintenance:

```text
We are planning for a scheduled maintenance during that time to <short action>. Service availability may be briefly affected during this period.
```

## Draft Format

Show the user a draft like this before scheduling:

```markdown
Draft only — **not scheduled**.

**Maintenance title**
`...`

**Scheduled window**

- **Start:** `...`
- **End:** `...`
- **Duration:** 60 minutes

**Status**
`NOTSTARTEDYET`

**Impact**
`UNDERMAINTENANCE`

**Message**
`...`

**Affected components**

- `component name` — `component_id`
- `component name` — `component_id`
```

## Scheduling Payload

When the user explicitly says to schedule it, create the maintenance on page
`cmmxeox60009t11pyrg02vclw`.

If using direct API, include `statuses` as well as `components`:

```json
{
"name": "upgrade of dev.certified.app (staging ePDS)",
"message": "We are planning for a scheduled maintenance during that time, to upgrade staging to 0.6.3, which applies a small cosmetic fix for consistent auth-service page branding.",
"components": ["cmmxf6unk000h1655r6yuwcl2", "cmmxfauhw00e0aa5b6pabeg9r"],
"statuses": [
{ "id": "cmmxf6unk000h1655r6yuwcl2", "status": "UNDERMAINTENANCE" },
{ "id": "cmmxfauhw00e0aa5b6pabeg9r", "status": "UNDERMAINTENANCE" }
],
"start": "YYYY-MM-DDTHH:mm:ss.000Z",
"end": "YYYY-MM-DDTHH:mm:ss.000Z",
"duration": 60,
"status": "NOTSTARTEDYET",
"notify": true,
"notifyStart": true,
"notifyEnd": true,
"notifyEarly": true,
"notifyMinutes": 30,
"autoStart": true,
"autoEnd": true,
"isCollapsed": true,
"expandAt": "YYYY-MM-DDTHH:mm:ss.000Z"
}
```

Set `expandAt` to three days before `start` when following recent scheduled
window style. In the sample above, `expandAt` uses the same timestamp format as
`start` / `end`, but the actual value should be three days earlier.

## Completing a Maintenance Window

Do **not** complete maintenance by only updating the parent maintenance record.
Instatus lifecycle changes are driven by **maintenance updates**. A parent
`PUT /v1/{page_id}/maintenances/{maintenance_id}` can accept fields and still
leave the maintenance in `NOTSTARTEDYET`.

When the user says to mark a window complete, add a maintenance update:

```text
POST https://api.instatus.com/v1/cmmxeox60009t11pyrg02vclw/maintenances/<maintenance_id>/maintenance-updates
Authorization: Bearer $INSTATUS_API_KEY
Content-Type: application/json
```

Completion payload shape:

```json
{
"message": "Upgrade has completed successfully.",
"components": ["cmmxf6unk000h1655r6yuwcl2", "cmmxfauhw00e0aa5b6pabeg9r"],
"statuses": [
{ "id": "cmmxf6unk000h1655r6yuwcl2", "status": "OPERATIONAL" },
{ "id": "cmmxfauhw00e0aa5b6pabeg9r", "status": "OPERATIONAL" }
],
"status": "COMPLETED",
"notify": true,
"started": "YYYY-MM-DDTHH:mm:ss.000Z"
}
```

Use a message matching the work type:

- Upgrade: `Upgrade has completed successfully.`
- Reconfiguration: `Configuration change has completed successfully.`
- Generic maintenance: `Maintenance has completed successfully.`

After posting the update, re-fetch the maintenance and verify:

- parent `status` is `COMPLETED`
- `resolved` is non-null
- affected components are `OPERATIONAL`
- the latest update has `status: COMPLETED`

## Direct API Fallback

The local `instatus-mcp` wrapper may reject create or update requests because
it omits required `components` / `statuses` fields, and its parent-maintenance
update path is not sufficient for lifecycle transitions. If that happens, call
the Instatus API directly with `INSTATUS_API_KEY` from the environment.

Create scheduled maintenance:

```text
POST https://api.instatus.com/v1/cmmxeox60009t11pyrg02vclw/maintenances
Authorization: Bearer $INSTATUS_API_KEY
Content-Type: application/json
```

Add lifecycle update:

```text
POST https://api.instatus.com/v1/cmmxeox60009t11pyrg02vclw/maintenances/<maintenance_id>/maintenance-updates
Authorization: Bearer $INSTATUS_API_KEY
Content-Type: application/json
```

After scheduling or completing, return only:

- Maintenance title
- Maintenance ID
- Window / resolved time
- Status
- Public URL: `https://certified.instatus.com/maintenance/<maintenance_id>`
Loading
Loading