Skip to content

Account erasure and export, and the 0.6 behaviour changes around it - #11

Merged
stsepelin merged 2 commits into
mainfrom
docs/account-deletion
Aug 22, 2026
Merged

stsepelin merged 2 commits into
mainfrom
docs/account-deletion

Conversation

@stsepelin

@stsepelin stsepelin commented Aug 22, 2026 •

Copy link
Copy Markdown
Owner

Docs for stsepelin/lukk#30 and stsepelin/lukk-js#51.

Adds the Account Deletion & Export page and moves the feature from Planned to Shipped.

Four things the surrounding pages still described wrongly

These were decided late enough that the existing pages had drifted:

  • Step-up confirmations are bound to the session that earned them. The confirmation page's diagram and prose both said "bound to this user", and account-deletion.md repeated it as the reason step-up alone can't keep a machine token out. That reason changed — a pin carrying lukk.account can still earn its own. Now documents the 423 confirmation_session_mismatch, why rotation doesn't invalidate a confirmation, and why strict matching keeps the co-issuer topology working.
  • The passkeys guard column, across passkeys / multiple-guards / upgrading. Framed as the sharpest of the guard-scoping guarantees rather than a tidiness one: the assertion lookup takes a credential id and no user, so it is the authentication decision. Both backfill directions are covered, including that removing a guard fails open.
  • features.gate_auth_routes does not switch off the erasure gate — that flag restores pre-0.6 reach, and these routes have no pre-0.6 behaviour to restore.
  • existsByCredentialId must stay unscoped in a replacement repository, since the unique index it backstops is global.

A third Known limitation

An erased identifier survives briefly as a rate-limiter cache key, bounded by rate_limits.login.decay_seconds. lukk doesn't sweep it: the per-address bucket can't be reconstructed after the fact, so a partial sweep would clear the tidy half and imply a completeness it doesn't have. Says so, and points at the two levers.

A trap worth its own callout

A queued AccountDeleting listener escapes the erasure rollback — it's pushed at dispatch, not at commit, so its work is on the wire when the transaction rolls back. Your domain data is gone and the account still exists. Queueing the event itself also serializes the subject's email, password hash and encrypted TOTP secret into jobs and failed_jobs, which nothing prunes.

Also fixed while here

  • Two broken anchors: #features → #feature-toggles, #denylist-store → #denylist.
  • An invalid [!DANGER] alert — not in the GFM/VitePress set, would have rendered as a plain blockquote. Now [!CAUTION].

pnpm docs:build clean. Anchors verified by slugifying every heading rather than trusting the build, which doesn't check them. No semicolons in the edited mermaid block.

Greptile Summary

Adds comprehensive documentation for account deletion and export and records the associated 0.6 behavior changes.

  • Adds the account deletion/export guide, navigation entry, configuration, and roadmap status.
  • Documents session-bound confirmations and account-erasure lifecycle and queueing constraints.
  • Expands passkey guard-scoping, migration, customization, and security guidance.

Confidence Score: 4/5

The PR appears safe to merge after minor documentation-reference inconsistencies are corrected.

The substantive documentation is internally coherent, but the feature-toggle table and shipped-composable inventory do not include newly documented public capabilities.

Files Needing Attention: docs/configuration.md, docs/roadmap.md

Important Files Changed

Filename Overview
docs/account-deletion.md Adds the account erasure/export reference, including authorization, lifecycle, extension events, export shape, client API, and known limitations.
docs/confirmation.md Clarifies that confirmation tokens bind to refresh-token families and documents mismatch responses, rotation, and co-issuer behavior.
docs/configuration.md Adds new feature-toggle keys and explanatory prose, but omits those keys from the adjacent reference table.
docs/multiple-guards.md Documents guard-scoped passkeys and lockout counters and explains the authentication boundary.
docs/customization.md Adds guard-scoping requirements for replacement passkey and refresh-token repositories.
docs/upgrading.md Replaces the highest-impact upgrade notice with passkey migration, guard-removal, and default-enabled account-deletion guidance.
docs/roadmap.md Moves account deletion/export to shipped but leaves useLukkAccount out of the shipped composable inventory.
docs/security.md Adds the bounded retention of erased identifiers in rate-limiter cache keys as a known limitation.

Sequence Diagram

sequenceDiagram
    participant Client
    participant API
    participant AppListener as AccountDeleting listener
    participant Database
    Client->>API: DELETE /auth/account
    API->>API: Validate ability and session confirmation
    API->>API: Revoke all sessions
    API->>Database: Begin erasure transaction
    API->>AppListener: Dispatch AccountDeleting synchronously
    AppListener->>Database: Erase application-owned data
    API->>Database: Erase auth artifacts and user
    Database-->>API: Commit
    API->>API: Dispatch AccountDeleted
    API-->>Client: Successful deletion
Loading

Comments Outside Diff (2)

  1. docs/configuration.md, line 194-206 (link)

    P2 Feature toggles missing from table

    The example introduces account_deletion, abilities, and gate_auth_routes, but the adjacent feature-reference table omits all three. Readers therefore cannot find their defaults and behavior in the reference table, including how to disable the newly default-enabled deletion route.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: docs/configuration.md
    Line: 194-206
    
    Comment:
    **Feature toggles missing from table**
    
    The example introduces `account_deletion`, `abilities`, and `gate_auth_routes`, but the adjacent feature-reference table omits all three. Readers therefore cannot find their defaults and behavior in the reference table, including how to disable the newly default-enabled deletion route.
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

    Fix in Claude Code

  2. docs/roadmap.md, line 50 (link)

    P2 Shipped composable inventory is incomplete

    The account-deletion page presents useLukkAccount() as the supported client API, but this inventory of shipped composables omits it. That makes the roadmap incorrectly suggest that account deletion and export lack a shipped Nuxt composable.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: docs/roadmap.md
    Line: 50
    
    Comment:
    **Shipped composable inventory is incomplete**
    
    The account-deletion page presents `useLukkAccount()` as the supported client API, but this inventory of shipped composables omits it. That makes the roadmap incorrectly suggest that account deletion and export lack a shipped Nuxt composable.
    
    
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

    Fix in Claude Code

Fix all with Greploop Fix All in Claude Code

Prompt To Fix All With AI
### Issue 1
docs/configuration.md:194-206
**Feature toggles missing from table**

The example introduces `account_deletion`, `abilities`, and `gate_auth_routes`, but the adjacent feature-reference table omits all three. Readers therefore cannot find their defaults and behavior in the reference table, including how to disable the newly default-enabled deletion route.

### Issue 2
docs/roadmap.md:50
**Shipped composable inventory is incomplete**

The account-deletion page presents `useLukkAccount()` as the supported client API, but this inventory of shipped composables omits it. That makes the roadmap incorrectly suggest that account deletion and export lack a shipped Nuxt composable.

```suggestion
- **Composables** — `useLukkAuth` (login + 2FA challenge + register + logout + sessions + restore + user), `useLukkTwoFactor`, `useLukkConfirmation`, `useLukkPasskeys`, `useLukkAccount` (account deletion + export), `useLukkEmailVerification`, `useLukkPasswordReset`, `useLukkFetch` (an auth-aware `$fetch`), and `useLukkForm` (Inertia-`useForm`-parity with nested errors + `rememberKey`).
```

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "docs: account erasure and export, and th..." | Re-trigger Greptile

…d it

Adds the Account Deletion & Export page (Art. 17 / Art. 15) and moves the
feature from Planned to Shipped.

Also documents four things decided late enough that the surrounding pages
still described the old behaviour:

- Step-up confirmations are bound to the session that earned them. The
  confirmation page's diagram and prose both said "bound to this user",
  and account-deletion.md repeated it as the reason step-up alone can't
  keep a machine token out. That reason changed: a pin carrying
  `lukk.account` can still earn its own. Documents the new 423
  `confirmation_session_mismatch`, why rotation doesn't invalidate a
  confirmation, and why strict matching keeps the co-issuer topology.
- The `passkeys` guard column, on passkeys, multiple-guards and upgrading.
  The assertion lookup takes a credential id and no user, so it is the
  authentication decision — this is the sharpest of the guard-scoping
  guarantees, not a tidiness one. Both backfill directions are covered,
  including that removing a guard fails open.
- `features.gate_auth_routes` does not switch off the erasure gate.
- `existsByCredentialId` must stay unscoped in a replacement repository,
  since the unique index it backstops is global.

Adds a third Known limitation: an erased identifier survives briefly as a
rate-limiter cache key, bounded by `rate_limits.login.decay_seconds`.
lukk doesn't sweep it because the per-address bucket can't be
reconstructed, and a partial sweep would imply a completeness it doesn't
have.

Warns that a queued `AccountDeleting` listener escapes the erasure
rollback, and that queueing the event serializes the subject's password
hash into `jobs` and `failed_jobs`.
SQL has no cross-connection transaction without two-phase commit, so a
user model on a different connection than lukk's tables gets its own.
lukk nests the disposal there, which covers anything that throws during
erasure, but a failure during the commit itself can leave the user erased
and lukk's rows restored.

The page promised all-or-nothing without qualification. It now says what
actually holds, and why the surviving state is the direction lukk prefers
anyway.
@stsepelin
stsepelin merged commit c346b32 into main Aug 22, 2026
2 checks passed
@stsepelin
stsepelin deleted the docs/account-deletion branch August 22, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant