this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

docs: add README with how-this-works summary

+42
+42
README.md
··· 1 + # portable.agency 2 + 3 + Link your platformed accounts to an Atmosphere account. 4 + 5 + Live at [link.portable.agency](https://link.portable.agency). First trial: linking User & Agents Discord members to their atproto accounts (preserving the "fascinator" role). 6 + 7 + ## How this works 8 + 9 + Each linkage is a *pair of records* on atproto PDSes — one under your control, one under portable.agency's. Those records are the only durable state; there's no service database to go stale or lose. 10 + 11 + 1. **Link a platformed account.** Authorize the external service (e.g. Discord) so we can confirm your membership and any relevant role. Nothing is written yet. 12 + 2. **Sign in with your Atmosphere account.** Fine-grained OAuth — we only request permission to write to the `agency.portable.membership` collection. 13 + 3. **Two records are written.** 14 + - An **attestation** (`agency.portable.attestation`) on portable.agency's PDS — a third-party statement that your DID owns the linked account. 15 + - A **claim** (`agency.portable.membership`) on your own PDS — a self-claim naming portable.agency as the attester. 16 + 17 + Both records carry the same `service` block. Matching them is the proof. 18 + 19 + **Multiple linkages.** Record keys are deterministic (hash of `did + service.type + community + identifier`), so re-linking the same external account is idempotent; linking a different account (e.g. a second Discord alt) creates a separate record. You can have N linkages per platform. 20 + 21 + **Where your state lives.** Nothing server-side except a short-lived cookie that holds the in-flight OAuth handshake. After a successful link, your browser stores just your DID in `localStorage`. When you load the page, the browser uses that DID to query your PDS directly and renders the linkages it finds — no server involvement. Clearing browser data only clears the view, not the linkages themselves. 22 + 23 + **Verification requires both halves.** The two records reference each other by DID, so confirming a linkage means fetching both — your claim from your PDS and portable.agency's attestation from its PDS. If portable.agency's PDS goes away without a repo backup, the attestation half is lost; your self-claim remains but becomes unverifiable on its own. Atproto repos are cryptographically signed, so anyone archiving portable.agency's repo could keep the attestations verifiable independently. 24 + 25 + **Unlinking.** Unlinking requires signing in with your Atmosphere account to prove ownership of the DID. Clicking Unlink redirects you to atproto OAuth; once you confirm, both records — the attestation on portable.agency's PDS and the matching claim on your own PDS — are deleted, leaving no orphan pointers. 26 + 27 + ## Run locally 28 + 29 + ```bash 30 + npm install 31 + cp .env.example .env # fill in secrets 32 + npm run dev 33 + ``` 34 + 35 + Note: atproto OAuth rejects localhost origins, so end-to-end testing needs a deployed HTTPS URL. The rest (record shapes, Discord handshake) can be exercised locally. 36 + 37 + ## Lexicons 38 + 39 + - [`agency.portable.membership`](lexicons/agency.portable.membership.json) — user's self-claim 40 + - [`agency.portable.attestation`](lexicons/agency.portable.attestation.json) — third-party attestation 41 + 42 + Served at `/lexicons/:nsid`.