DreamLake

Vault metadata pagination

Status: Released in CLI 0.16.0 and Python 0.13.0; staged backend and hosted desktop/mobile pagination acceptance passed. Production backend acceptance remains separate. Tracking: Vault #241.

List returns metadata only. It never decrypts entries or generates OTP codes. CLI and Python keep their complete-list behavior, following pages of 100 entries. Retired entries are omitted unless the owner explicitly includes them. Expired entries remain visible as metadata with expiresAt; listing grants no secret access.

shell
dreamlake vault -p alice/remote list
# Owner-only: also show retired metadata.
dreamlake vault list -p alice/remote --include-deleted

For a browser, TUI, or batch job, request one page at a time. CLI --limit or --cursor selects one-page output, including nextCursor. Python list_page returns the same object. nextCursor: null marks completion. Keep the prefix, account/access key, and retired filter unchanged when continuing. A page contains 1–200 entries at most (100 by default); an empty final page is valid.

shell
dreamlake vault -p alice/remote list --limit 50 > first-page.json
# The cursor is metadata, not a credential.
cursor=$(jq -r '.nextCursor // empty' first-page.json)
if [ -n "$cursor" ]; then
  dreamlake vault -p alice/remote list --limit 50 --cursor "$cursor"
fi

HTTP: GET /v1/vault/entries?prefix=alice/remote&limit=50 returns {entries, nextCursor}. Continue with the returned cursor query parameter; includeDeleted=true is owner-only. Page size may change between requests. Cursors are validated query positions bound to the account, prefix, filter and access-key scopes, not authorization grants or encrypted secrets. Every page reauthorizes and filters in Mongo before applying its limit. Editing a valid position can only skip already-authorized names; clients should treat it as opaque. Malformed or mismatched cursors return HTTP 400 without echoed input.

Ordering and concurrent changes

Names use binary ASCII ordering, with one name per tenant. Traversal is not a snapshot: new names after the last returned name may appear; names inserted before it require a fresh listing. Retired or deleted entries may disappear between pages. Deleting the boundary entry does not invalidate the cursor. A replaced entry at the same already-returned name is not revisited. No cursor retains a read grant; revoked/expired access keys are checked again, and recreated entries do not inherit a scoped key's old identity permission.

Compatibility and delivery boundary

Requests without limit or cursor retain the legacy unbounded response for older HTTP/UI clients. New clients tolerate an older server that omits nextCursor, treating its response as complete. This compatibility behavior is not a claim that all API listing is bounded. The deployed UI uses dreamlake-ai/lib/vault.ts (createVaultClient.list); hosted staging verification passed at d44ca49 against backend c49dc1d. Legacy route removal remains a separate compatibility decision.

Tests cover 207 entries, cross-client continuation, prefix/account/scope isolation, retired and expired metadata, invalid cursors, recreated entry identity, and concurrent insert/delete behavior in real Mongo/HTTP. Client regressions reject non-progressing pages and strip unsolicited secret fields. Isolated fixture acceptance is separate from production deployment and browser acceptance.