| … | … | @@ -132,12 +132,16 @@ export async function recoverCommunityRepo ({ rootMnemonic, founderDeviceMnemoni |
| 132 | 132 | // repo may write; the linearized view converges. rkeys are caller-chosen (the feedgen rkey = the feed's |
| 133 | 133 | // short name; the labeler is the reserved `self`). createdAt is caller-supplied for deterministic tests. |
| 134 | 134 | |
| 135 | // app.bsky.feed.generator — declares a custom feed owned by this community DID. `did` = the SERVICE DID | |
| 136 | // that answers getFeedSkeleton; in the self-custody model the community DID IS its own service (its PLC | |
| 137 | // doc carries #bsky_fg → the favela-web endpoint), so did = communityDid. Returns the record. | |
| 138 | export async function publishFeedGenerator (repo, communityDid, { rkey, displayName, description = '', avatar = null, createdAt = new Date().toISOString() } = {}) { | |
| 135 | // app.bsky.feed.generator — the feed record, OWNED by this community/group (it lives in this repo, so its | |
| 136 | // creator = the group account). The record's `did` field is the getFeedSkeleton SERVICE, which — like every | |
| 137 | // real feed (Discover→did:web:discover.bsky.app, Artists→did:web:api.graze.social) — is a shared `did:web` | |
| 138 | // (favela-web), NOT the group's own DID. Pass `service` (the did:web); the group stays the OWNER + name, the | |
| 139 | // did:web is the invisible compute backend. (Falls back to communityDid only for legacy callers.) The feed | |
| 140 | // resolves as "<displayName> by <group handle>", computed by the did:web. Returns the record. | |
| 141 | export async function publishFeedGenerator (repo, communityDid, { rkey, displayName, service = null, description = '', avatar = null, createdAt = new Date().toISOString() } = {}) { | |
| 139 | 142 | if (!repo || !communityDid || !rkey || !displayName) throw new Error('publishFeedGenerator: repo, communityDid, rkey, displayName required') |
| 140 | const record = { $type: 'app.bsky.feed.generator', did: communityDid, displayName, createdAt } | |
| 143 | const serviceDid = service || communityDid // service SHOULD be a did:web (favela-web); communityDid is the legacy self-service fallback | |
| 144 | const record = { $type: 'app.bsky.feed.generator', did: serviceDid, displayName, createdAt } | |
| 141 | 145 | if (description) record.description = description |
| 142 | 146 | if (avatar) record.avatar = avatar |
| 143 | 147 | await repo.putRecord('app.bsky.feed.generator', String(rkey), record) |
| … | … | @@ -26,12 +26,16 @@ assert(op.services.bsky_fg && op.services.bsky_fg.type === 'BskyFeedGenerator', |
| 26 | 26 | assert(op.services.atproto_labeler && op.services.atproto_labeler.type === 'AtprotoLabeler', 'genesis declares #atproto_labeler → the membrane') |
| 27 | 27 | assert(op.verificationMethods.atproto_label === op.verificationMethods.atproto, '#atproto_label VM = the shared #atproto signing key (labels co-signed by it)') |
| 28 | 28 | |
| 29 | // ── app.bsky.feed.generator ── | |
| 30 | const gen = await publishFeedGenerator(c.repo, c.did, { rkey: 'catlovers', displayName: 'Cat Lovers', description: 'meow', createdAt: CTS }) | |
| 31 | assert(gen.did === c.did, 'feed.generator.did = the community DID (self-custody: the DID is its own service)') | |
| 29 | // ── app.bsky.feed.generator — the group OWNS the record; the record's `did` = a did:web SERVICE ── | |
| 30 | const SERVICE = 'did:web:favela.example.net' | |
| 31 | const gen = await publishFeedGenerator(c.repo, c.did, { rkey: 'catlovers', displayName: 'Cat Lovers', description: 'meow', service: SERVICE, createdAt: CTS }) | |
| 32 | assert(gen.did === SERVICE, 'feed.generator.did = the did:web SERVICE (favela-web), NOT the group DID — group is the OWNER, did:web is the invisible engine (like graze.social)') | |
| 32 | 33 | const genBack = await c.repo.getRecord('app.bsky.feed.generator', 'catlovers') |
| 33 | assert(genBack && genBack.$type === 'app.bsky.feed.generator' && genBack.did === c.did, 'feed.generator read back with $type + did') | |
| 34 | assert(genBack && genBack.$type === 'app.bsky.feed.generator' && genBack.did === SERVICE, 'feed.generator read back: $type + did:web service') | |
| 34 | 35 | assert(genBack.displayName === 'Cat Lovers' && genBack.description === 'meow' && genBack.createdAt === CTS, 'feed.generator carries displayName/description/createdAt') |
| 36 | // the record lives in c.repo (= the group's repo) → the group is the CREATOR/owner; only the service is the did:web | |
| 37 | const legacy = await publishFeedGenerator(c.repo, c.did, { rkey: 'legacyfeed', displayName: 'Legacy', createdAt: CTS }) | |
| 38 | assert(legacy.did === c.did, 'legacy fallback: no service → did = communityDid (back-compat for old callers)') | |
| 35 | 39 | |
| 36 | 40 | // ── app.bsky.labeler.service (rkey self) ── |
| 37 | 41 | const lab = await publishLabelerService(c.repo, { labelValues: ['spam', '!hide'], createdAt: CTS }) |