···131131132132| Lexicon ID | Published by | Purpose |
133133| ------------------------------------------- | ------------ | ------------------------------------------------------------------------ |
134134-| `site.exosphere.sphere` | Sphere owner | Sphere declaration (name, slug, visibility, modules) — enables discovery |
134134+| `site.exosphere.sphere.profile` | Sphere owner | Sphere declaration (name, slug, visibility, modules) — enables discovery |
135135| `site.exosphere.sphere.member` | Member | "I am a member of this Sphere" — member-side of bilateral membership |
136136| `site.exosphere.sphere.memberApproval` | Owner/admin | "This user is an approved member" — admin-side of bilateral membership |
137137| `site.exosphere.sphere.permissions` | Sphere owner | Permission overrides for core Sphere actions |
138138-| `site.exosphere.featureRequest` | Author | Feature request content |
138138+| `site.exosphere.featureRequest.entry` | Author | Feature request content |
139139| `site.exosphere.featureRequest.vote` | Voter | Upvote on a feature request |
140140| `site.exosphere.featureRequest.comment` | Commenter | Comment on a feature request |
141141| `site.exosphere.featureRequest.commentVote` | Voter | Upvote on a comment |
···173173174174### Sphere declaration on PDS
175175176176-When a Sphere is created, the owner publishes a `site.exosphere.sphere` record on their PDS. This makes the Sphere discoverable by anyone crawling the AT Protocol network and serves as the canonical reference that other records (membership, content) point to via AT URI.
176176+When a Sphere is created, the owner publishes a `site.exosphere.sphere.profile` record on their PDS. This makes the Sphere discoverable by anyone crawling the AT Protocol network and serves as the canonical reference that other records (membership, content) point to via AT URI.
177177178178### Private Spheres
179179···308308309309### Private Spheres and membership privacy
310310311311-For **private Spheres**, the Sphere record itself (`site.exosphere.sphere`) has `visibility: "private"`. The bilateral membership records are still published on PDS (since AT Protocol repos are public), but the Sphere's content remains off-protocol. A third party could see that a user is a member of a private Sphere, but cannot access the Sphere's content. This is an acceptable trade-off — membership is public, content is private — similar to how a private GitHub repository's collaborator list can be partially visible.
311311+For **private Spheres**, the Sphere record itself (`site.exosphere.sphere.profile`) has `visibility: "private"`. The bilateral membership records are still published on PDS (since AT Protocol repos are public), but the Sphere's content remains off-protocol. A third party could see that a user is a member of a private Sphere, but cannot access the Sphere's content. This is an acceptable trade-off — membership is public, content is private — similar to how a private GitHub repository's collaborator list can be partially visible.
312312313313If full membership privacy is required, operators can skip PDS writes for membership and rely solely on the local SQLite table. This sacrifices interoperability for privacy.
314314
+1-1
TODO.md
···6060## Lexicons
61616262- [x] Define formal Lexicon schemas for all AT Protocol record types (exact record shapes are still being defined)
6363- - [x] `site.exosphere.featureRequest`
6363+ - [x] `site.exosphere.featureRequest.entry`
6464 - [ ] Feed/post record types
6565- [ ] Set up Lexicon codegen for type-safe record handling
6666
+27-27
packages/core/src/generated/lexicon-records.ts
···11// AUTO-GENERATED from landing/lexicons/site/exosphere/*.json
22// Do not edit manually. Run: bun run generate:lexicons
3344-export interface FeatureRequestRecord {
55- title: string;
66- description?: string;
77- category?: string;
88- /** did — DID of the Sphere owner (the identity hosting the site.exosphere.sphere record). */
99- subject: string;
1010- /** datetime */
1111- updatedAt?: string;
1212-}
1313-144export interface FeatureRequestCommentRecord {
155 /** at-uri — AT URI of the feature request being commented on. */
166 subject: string;
···2212export interface FeatureRequestCommentVoteRecord {
2313 /** at-uri — AT URI of the comment being voted on. */
2414 subject: string;
1515+}
1616+1717+export interface FeatureRequestEntryRecord {
1818+ title: string;
1919+ description?: string;
2020+ category?: string;
2121+ /** did — DID of the Sphere owner (the identity hosting the site.exosphere.sphere.profile record). */
2222+ subject: string;
2323+ /** datetime */
2424+ updatedAt?: string;
2525}
26262727export interface FeatureRequestPermissionsRecord {
···5858 action: string;
5959}
60606161-export interface SphereRecord {
6262- /** Human-readable display name. */
6363- name: string;
6464- /** Short description of the Sphere's purpose. */
6565- description?: string;
6666- /** Whether the Sphere's content is publicly readable. */
6767- visibility: string;
6868- /** Module names enabled for this Sphere. */
6969- modules?: string[];
7070- /** datetime */
7171- createdAt: string;
7272-}
7373-7461export interface SphereMemberRecord {
7575- /** at-uri — AT URI of the Sphere record (site.exosphere.sphere) on the owner's PDS. */
6262+ /** at-uri — AT URI of the Sphere record (site.exosphere.sphere.profile) on the owner's PDS. */
7663 sphere: string;
7764}
78657966export interface SphereMemberApprovalRecord {
8080- /** at-uri — AT URI of the Sphere record (site.exosphere.sphere). */
6767+ /** at-uri — AT URI of the Sphere record (site.exosphere.sphere.profile). */
8168 sphere: string;
8269 /** did — DID of the member being approved. */
8370 subject: string;
···10087 updatePermissions?: string;
10188}
102899090+export interface SphereProfileRecord {
9191+ /** Human-readable display name. */
9292+ name: string;
9393+ /** Short description of the Sphere's purpose. */
9494+ description?: string;
9595+ /** Whether the Sphere's content is publicly readable. */
9696+ visibility: string;
9797+ /** Module names enabled for this Sphere. */
9898+ modules?: string[];
9999+ /** datetime */
100100+ createdAt: string;
101101+}
102102+103103export interface PdsRecordMap {
104104- "site.exosphere.featureRequest": FeatureRequestRecord;
105104 "site.exosphere.featureRequest.comment": FeatureRequestCommentRecord;
106105 "site.exosphere.featureRequest.commentVote": FeatureRequestCommentVoteRecord;
106106+ "site.exosphere.featureRequest.entry": FeatureRequestEntryRecord;
107107 "site.exosphere.featureRequest.permissions": FeatureRequestPermissionsRecord;
108108 "site.exosphere.featureRequest.status": FeatureRequestStatusRecord;
109109 "site.exosphere.featureRequest.vote": FeatureRequestVoteRecord;
110110 "site.exosphere.moderation": ModerationRecord;
111111- "site.exosphere.sphere": SphereRecord;
112111 "site.exosphere.sphere.member": SphereMemberRecord;
113112 "site.exosphere.sphere.memberApproval": SphereMemberApprovalRecord;
114113 "site.exosphere.sphere.permissions": SpherePermissionsRecord;
114114+ "site.exosphere.sphere.profile": SphereProfileRecord;
115115}
···99import { enableModuleSchema } from "../schemas.ts";
1010import { findSphere, getEnabledModules, formatModules } from "./helpers.ts";
11111212-const SPHERE_COLLECTION = "site.exosphere.sphere" as const;
1212+const SPHERE_COLLECTION = "site.exosphere.sphere.profile" as const;
13131414type Sphere = typeof spheres.$inferSelect;
1515
+1-1
packages/core/src/sphere/api/spheres.ts
···1010import { computeUserPermissions } from "../../permissions/check.ts";
1111import { findSphere, getEnabledModules, formatModules } from "./helpers.ts";
12121313-const SPHERE_COLLECTION = "site.exosphere.sphere";
1313+const SPHERE_COLLECTION = "site.exosphere.sphere.profile";
14141515/** Return spheres the given DID is an active member of, including their role. */
1616export function getMemberSpheres(did: string) {