Releases: ascorbic/cirrus
@getcirrus/pds@0.15.1
@getcirrus/pds@0.15.0
Minor Changes
- #160
a492bf7Thanks @ascorbic! - Lexicon validation now matches the reference PDS more closely:createRecord,putRecord, andapplyWriteshonor thevalidateflag from the request body.truerequires a known schema,falseskips schema validation,undefinedvalidates known schemas optimistically.- Responses include
validationStatus("valid"for known,"unknown"for unknown collections; omitted whenvalidate: false). Per-writevalidationStatusis returned inapplyWritesresults. - The record's
$typeis filled in fromcollectionwhen missing and rejected on mismatch. - Generic record-key shape (
isRecordKey) is enforced for any provided rkey, regardless ofvalidateflag — closes a hole where empty-string and path-traversal-style rkeys could reach the repo. - Schema-specific record keys are validated against the schema's
keySchemafor known collections (e.g.app.bsky.feed.postrequires a TID,app.bsky.actor.profilerequiresself). - Legacy
{ cid, mimeType }blob refs are rejected. - Bundled schema set broadened to include
com.atproto.lexicon.schema,app.bsky.actor.status,app.bsky.notification.declaration, andchat.bsky.actor.declaration. - The Durable Object is now the authoritative rkey allocator: when the client doesn't supply an rkey, the worker validates against a candidate (so restrictive
keySchemas still reject early) and the DO picks the final rkey against its MST state, with a small retry loop to defeat any worker-isolate clockid collisions. - Client-supplied rkey collisions return
409 RecordAlreadyExistsinstead of a generic 500. - Intra-batch duplicate rkeys in
applyWritesreturn400 InvalidRequest(distinguished from the 409 above). - Missing rkey for
applyWrites#update/#deletereturns400 InvalidRequest. - Non-boolean
validateflag values return400 InvalidRequest. - Non-string
rkeyvalues (includingnull) return400 InvalidRequest.
Patch Changes
-
#162
5920074Thanks @ascorbic! - Fix relay desync after a failed write (e.g. an image post that errors mid-flight).applyWriteswas assigning the newRepoto in-memory state before sequencing the firehose event. If anything threw between then andsequenceCommitsucceeding, Cloudflare rolled back the SQLite writes but the in-memoryRepostayed advanced. The next successful write then emitted a firehose commit whosesincerev the relay had never seen, and the relay marked the repo desynced — requiring a manualrequestCrawlto recover.this.repois now only assigned after the sequence + broadcast succeed, and any failure in that window invalidates the in-memory cache so the next access reloads from storage.
@getcirrus/pds@0.14.0
Minor Changes
-
#158
ec935b1Thanks @ascorbic! - Support granular OAuth permissions and permission sets per the atproto permission spec.repo:,rpc:,blob:,account:,identity:scopes are parsed and enforced (via@atproto/oauth-scopes);transition:generic/transition:email/transition:chat.bskykeep working through the transitional shim.verifyAccessTokennow accepts a(perms) => p.assertRepo({ collection, action })-style check callback in addition to the legacy required-scope string.- PDS write endpoints (
createRecord,putRecord,deleteRecord,applyWrites,uploadBlob) assert the matching scope before dispatching. include:NSID?aud=...permission-set scopes are resolved via@atcute/lexicon-resolverand expanded inline at code-issuance time, so resource-server checks never need network access. The PDS caches resolved permission sets in DO SQLite with the spec's stale-while-revalidate semantics (24h soft / 90d hard).- The consent UI groups long granular-scope lists by NSID authority and collapses them behind a
<details>disclosure, so a 30-scope client like tangled.org renders as a few audit-friendly lines instead of a wall of text.include:scopes render the resolved bundle's title/detail.
Note on legacy auth: session JWTs (from
createSession/ app-password flow), service JWTs, and the staticAUTH_TOKENcontinue to bypass scope checks at resource handlers — they're treated as fully-trusted callers per their original semantics (app-password equivalents). The newrpc:proxy enforcement only applies to OAuth (DPoP) tokens; legacy clients can still call any AppView method via the proxy regardless of granular scopes.
Patch Changes
-
#153
6e4d81dThanks @georgemblack! - Fixcom.atproto.server.checkAccountStatusresponse to be lexicon-compliant:privateStateValuesis a requiredinteger(not nullable), so return0instead ofnullin both the activated and not-activated branches. -
#155
d1a7074Thanks @a-lavis! - Fix two OAuth token refresh bugs that prevented spec-compliant clients (e.g. tangled.org via indigo) from refreshing their session after the access token expired.- Track access and refresh expiry separately on
TokenData(accessExpiresAt/refreshExpiresAt) instead of a singleexpiresAt.cleanup()now prunes byrefreshExpiresAt, so a row isn't deleted while its refresh token is still valid. The PDS SQLite store migrates legacyoauth_tokensrows in place, derivingrefresh_expires_atasMAX(expires_at, issued_at + REFRESH_TOKEN_TTL). - The PDS auth middleware now sends
WWW-Authenticate: DPoP error="invalid_token"on 401 responses for invalid/expired OAuth access tokens, as required by the atproto XRPC spec. Clients that gate refresh on this header (indigo, and others) will now refresh automatically instead of staying logged-in-but-broken until the user signs out.
- Track access and refresh expiry separately on
-
Updated dependencies [
ec935b1,d1a7074]:- @getcirrus/oauth-provider@0.4.0
@getcirrus/oauth-provider@0.4.0
Minor Changes
-
#158
ec935b1Thanks @ascorbic! - Support granular OAuth permissions and permission sets per the atproto permission spec.repo:,rpc:,blob:,account:,identity:scopes are parsed and enforced (via@atproto/oauth-scopes);transition:generic/transition:email/transition:chat.bskykeep working through the transitional shim.verifyAccessTokennow accepts a(perms) => p.assertRepo({ collection, action })-style check callback in addition to the legacy required-scope string.- PDS write endpoints (
createRecord,putRecord,deleteRecord,applyWrites,uploadBlob) assert the matching scope before dispatching. include:NSID?aud=...permission-set scopes are resolved via@atcute/lexicon-resolverand expanded inline at code-issuance time, so resource-server checks never need network access. The PDS caches resolved permission sets in DO SQLite with the spec's stale-while-revalidate semantics (24h soft / 90d hard).- The consent UI groups long granular-scope lists by NSID authority and collapses them behind a
<details>disclosure, so a 30-scope client like tangled.org renders as a few audit-friendly lines instead of a wall of text.include:scopes render the resolved bundle's title/detail.
Note on legacy auth: session JWTs (from
createSession/ app-password flow), service JWTs, and the staticAUTH_TOKENcontinue to bypass scope checks at resource handlers — they're treated as fully-trusted callers per their original semantics (app-password equivalents). The newrpc:proxy enforcement only applies to OAuth (DPoP) tokens; legacy clients can still call any AppView method via the proxy regardless of granular scopes.
Patch Changes
- #155
d1a7074Thanks @a-lavis! - Fix two OAuth token refresh bugs that prevented spec-compliant clients (e.g. tangled.org via indigo) from refreshing their session after the access token expired.- Track access and refresh expiry separately on
TokenData(accessExpiresAt/refreshExpiresAt) instead of a singleexpiresAt.cleanup()now prunes byrefreshExpiresAt, so a row isn't deleted while its refresh token is still valid. The PDS SQLite store migrates legacyoauth_tokensrows in place, derivingrefresh_expires_atasMAX(expires_at, issued_at + REFRESH_TOKEN_TTL). - The PDS auth middleware now sends
WWW-Authenticate: DPoP error="invalid_token"on 401 responses for invalid/expired OAuth access tokens, as required by the atproto XRPC spec. Clients that gate refresh on this header (indigo, and others) will now refresh automatically instead of staying logged-in-but-broken until the user signs out.
- Track access and refresh expiry separately on
@getcirrus/pds@0.13.0
create-pds@0.0.12
@getcirrus/pds@0.12.0
@getcirrus/pds@0.11.0
Minor Changes
-
#137
90e9771Thanks @ascorbic! - Add option to auto-generate a password duringpds initandpds secret password, with clipboard copy support -
#136
287c971Thanks @ascorbic! - Add live terminal dashboard for PDS monitoring viapds dashboard. Shows repository stats, federation sync status, firehose subscribers with IPs, real-time event log, and notifications. Also adds a web dashboard at/status.
@getcirrus/pds@0.10.6
Patch Changes
-
#134
127f3dbThanks @ascorbic! - Fix OAuth client metadata caching to avoid redundant network requestsClient metadata was re-fetched from the network on every OAuth request instead of using the cache, adding latency to token exchanges and making auth fragile when client metadata endpoints are slow or unavailable.
-
Updated dependencies [
e76f1e4,127f3db]:- @getcirrus/oauth-provider@0.3.2
@getcirrus/oauth-provider@0.3.2
Patch Changes
-
#132
e76f1e4Thanks @ascorbic! - Fix OAuth client authentication failures for public clients and mixed JWKS- Fix
invalid_clienterror for clients that omittoken_endpoint_auth_methodin their metadata (Zod default ofclient_secret_basicwas passed through unsupported) - Fix
invalid usage "encrypt"error when client JWKS contains both signing and encryption keys by using jose'screateLocalJWKSetfor proper key selection
- Fix
-
#134
127f3dbThanks @ascorbic! - Fix OAuth authentication failure for confidential clients whose JWKS contains invalid key_opsClients with ECDSA signing keys that incorrectly declare encryption operations (e.g.
"encrypt","wrapKey") in their JWKSkey_opsfield would fail with "invalid usage" during token exchange.