commits
- add src/publish-lexicons.js + publish:lexicons script to push
agency.portable.{membership,attestation} as com.atproto.lexicon.schema
records on the attester's PDS (resolves via _lexicon.portable.agency DNS)
- normalize handle input in /login: strip leading @, extract from
bsky.app/profile URLs, drop trailing dot; catch resolve errors to
show a friendly message instead of 500
- validate NSID format in /lexicons/:nsid to block path traversal
- hide #rows on load when localStorage has a DID, unhide after client
render completes — stops the empty-form flash before populated view
- pin Railway service in npm scripts (deploy, logs) so the CLI
doesn't prompt when link state is lost
The linked-state view is now rendered client-side from the user's PDS, with the DID held in browser localStorage, so the server holds no long-lived session and the view survives cookie clears. Unlink now triggers fresh atproto OAuth and deletes both the attestation (via attester creds) and the user's claim (via the just-returned OAuth session), removing orphan-pointer cases.
Pairs a self-claim on the user's PDS with a third-party attestation on portable.agency's PDS. First trial: User and Agents Discord fascinators.
- add src/publish-lexicons.js + publish:lexicons script to push
agency.portable.{membership,attestation} as com.atproto.lexicon.schema
records on the attester's PDS (resolves via _lexicon.portable.agency DNS)
- normalize handle input in /login: strip leading @, extract from
bsky.app/profile URLs, drop trailing dot; catch resolve errors to
show a friendly message instead of 500
- validate NSID format in /lexicons/:nsid to block path traversal
- hide #rows on load when localStorage has a DID, unhide after client
render completes — stops the empty-form flash before populated view
- pin Railway service in npm scripts (deploy, logs) so the CLI
doesn't prompt when link state is lost
The linked-state view is now rendered client-side from the user's PDS, with the DID held in browser localStorage, so the server holds no long-lived session and the view survives cookie clears. Unlink now triggers fresh atproto OAuth and deletes both the attestation (via attester creds) and the user's claim (via the just-returned OAuth session), removing orphan-pointer cases.