fix(spaces): default visibility to private (sovereign by default)
Spaces with missing/undefined visibility were falling through to "public" in 7 places: normalizeVisibility fallback, migrateVisibility early return, renderShell default, getSpaceConfig, space list APIs, and the HTML injection middleware. All now default to "private". The migrateVisibility function now writes "private" to docs with missing visibility on load. Also fixed jeff and hash spaces on production (were undefined → private). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7c29ccea41
commit
ca9e91651c
|
|
@ -23,7 +23,7 @@ export function normalizeVisibility(v: string): SpaceVisibility {
|
|||
if (v === 'public_read' || v === 'public') return 'public';
|
||||
if (v === 'authenticated' || v === 'permissioned') return 'permissioned';
|
||||
if (v === 'members_only' || v === 'private') return 'private';
|
||||
return 'public';
|
||||
return 'private'; // sovereign by default — unknown values treated as private
|
||||
}
|
||||
|
||||
// ── Nest Permissions & Policy ──
|
||||
|
|
@ -237,7 +237,13 @@ function migrateVisibility(
|
|||
slug: string,
|
||||
): Automerge.Doc<CommunityDoc> {
|
||||
const v = doc.meta?.visibility as string;
|
||||
if (!v) return doc;
|
||||
if (!v) {
|
||||
// Missing visibility — default to private (sovereign by default)
|
||||
console.log(`[Store] Migrating missing visibility→private in ${slug}`);
|
||||
return Automerge.change(doc, `Set default visibility to private in ${slug}`, (d) => {
|
||||
d.meta.visibility = 'private';
|
||||
});
|
||||
}
|
||||
const normalized = normalizeVisibility(v);
|
||||
if (v === normalized) return doc;
|
||||
console.log(`[Store] Migrating visibility ${v}→${normalized} in ${slug}`);
|
||||
|
|
|
|||
|
|
@ -464,7 +464,7 @@ async function getSpaceConfig(slug: string): Promise<SpaceAuthConfig | null> {
|
|||
if (!doc) return null;
|
||||
return {
|
||||
spaceSlug: slug,
|
||||
visibility: (doc.meta.visibility || "public") as SpaceVisibility,
|
||||
visibility: (doc.meta.visibility || "private") as SpaceVisibility,
|
||||
ownerDID: doc.meta.ownerDID || undefined,
|
||||
app: "rspace",
|
||||
};
|
||||
|
|
@ -1960,12 +1960,12 @@ app.use("/:space/*", async (c, next) => {
|
|||
const space = c.req.param("space");
|
||||
if (!space || space === "api" || space.includes(".")) return;
|
||||
const config = await getSpaceConfig(space);
|
||||
const vis = config?.visibility || "public";
|
||||
if (vis === "public") return;
|
||||
const vis = config?.visibility || "private";
|
||||
if (vis === "private") return; // Shell already defaults to private
|
||||
const html = await c.res.text();
|
||||
c.res = new Response(
|
||||
html.replace(
|
||||
'data-space-visibility="public"',
|
||||
'data-space-visibility="private"',
|
||||
`data-space-visibility="${vis}"`,
|
||||
),
|
||||
{ status: c.res.status, headers: c.res.headers },
|
||||
|
|
@ -2179,7 +2179,7 @@ app.get("/admin-data", async (c) => {
|
|||
spacesList.push({
|
||||
slug: data.meta.slug,
|
||||
name: data.meta.name,
|
||||
visibility: data.meta.visibility || "public",
|
||||
visibility: data.meta.visibility || "private",
|
||||
createdAt: data.meta.createdAt,
|
||||
ownerDID: data.meta.ownerDID,
|
||||
shapeCount,
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ export function renderShell(opts: ShellOptions): string {
|
|||
modules,
|
||||
theme = "dark",
|
||||
head = "",
|
||||
spaceVisibility = "public",
|
||||
spaceVisibility = "private",
|
||||
} = opts;
|
||||
|
||||
// Auto-populate from space data when not explicitly provided
|
||||
|
|
|
|||
|
|
@ -240,7 +240,7 @@ spaces.get("/", async (c) => {
|
|||
if (seenSlugs.has(slug)) continue;
|
||||
seenSlugs.add(slug);
|
||||
|
||||
let vis = data.meta.visibility || "public";
|
||||
let vis = data.meta.visibility || "private";
|
||||
// Check both claims.sub (raw userId) and did:key: format for
|
||||
// compatibility — auto-provisioned spaces store ownerDID as did:key:
|
||||
const callerDid = claims ? ((claims.did as string) || `did:key:${(claims.sub as string).slice(0, 32)}`) : "";
|
||||
|
|
@ -298,8 +298,8 @@ spaces.get("/", async (c) => {
|
|||
// Within each group: user's own spaces first, then alphabetically
|
||||
const visOrder: Record<string, number> = { private: 0, permissioned: 1, public: 2 };
|
||||
spacesList.sort((a, b) => {
|
||||
const va = visOrder[a.visibility || "public"] ?? 2;
|
||||
const vb = visOrder[b.visibility || "public"] ?? 2;
|
||||
const va = visOrder[a.visibility || "private"] ?? 0;
|
||||
const vb = visOrder[b.visibility || "private"] ?? 0;
|
||||
if (va !== vb) return va - vb;
|
||||
if (a.role && !b.role) return -1;
|
||||
if (!a.role && b.role) return 1;
|
||||
|
|
@ -522,7 +522,7 @@ spaces.get("/admin", async (c) => {
|
|||
spacesList.push({
|
||||
slug: data.meta.slug,
|
||||
name: data.meta.name,
|
||||
visibility: data.meta.visibility || "public",
|
||||
visibility: data.meta.visibility || "private",
|
||||
createdAt: data.meta.createdAt,
|
||||
ownerDID: data.meta.ownerDID,
|
||||
shapeCount,
|
||||
|
|
|
|||
Loading…
Reference in New Issue