Skip to content

Commit 77d0d80

Browse files
fix: use existing generatePublicReadAcl, skip seed in read-only mode
- Import generatePublicReadAcl + serializeAcl from wac/parser.js instead of inlining ACL JSON (it exists and does exactly what we need) - Skip the onReady seeding hook when options.readOnly is true so a read-only deployment does not mutate DATA_ROOT
1 parent 020aff9 commit 77d0d80

2 files changed

Lines changed: 31 additions & 46 deletions

File tree

src/server.js

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -548,32 +548,35 @@ export function createServer(options = {}) {
548548

549549
// Server-root landing page: seed /index.html and /.acl on first start
550550
// (skip-if-exists, operator customisations preserved). See #276.
551-
fastify.addHook('onReady', async () => {
552-
try {
553-
const pkg = await readFile(join(__dirname, '..', 'package.json'), 'utf8');
554-
const { version } = JSON.parse(pkg);
555-
await seedServerRoot({
556-
version,
557-
singleUser,
558-
idp: idpEnabled,
559-
singleUserName,
560-
enabled: {
551+
// Skipped in read-only mode — startup must not mutate DATA_ROOT.
552+
if (!options.readOnly) {
553+
fastify.addHook('onReady', async () => {
554+
try {
555+
const pkg = await readFile(join(__dirname, '..', 'package.json'), 'utf8');
556+
const { version } = JSON.parse(pkg);
557+
await seedServerRoot({
558+
version,
559+
singleUser,
561560
idp: idpEnabled,
562-
nostr: nostrEnabled,
563-
webrtc: webrtcEnabled,
564-
activitypub: activitypubEnabled,
565-
git: gitEnabled,
566-
pay: payEnabled,
567-
notifications: notificationsEnabled,
568-
mashlib: mashlibEnabled,
569-
mongo: mongoEnabled,
570-
tunnel: tunnelEnabled
571-
}
572-
});
573-
} catch (err) {
574-
fastify.log.warn({ err }, 'Failed to seed server root');
575-
}
576-
});
561+
singleUserName,
562+
enabled: {
563+
idp: idpEnabled,
564+
nostr: nostrEnabled,
565+
webrtc: webrtcEnabled,
566+
activitypub: activitypubEnabled,
567+
git: gitEnabled,
568+
pay: payEnabled,
569+
notifications: notificationsEnabled,
570+
mashlib: mashlibEnabled,
571+
mongo: mongoEnabled,
572+
tunnel: tunnelEnabled
573+
}
574+
});
575+
} catch (err) {
576+
fastify.log.warn({ err }, 'Failed to seed server root');
577+
}
578+
});
579+
}
577580

578581
// Single-user mode: create pod on startup if it doesn't exist
579582
if (singleUser) {

src/ui/server-root.js

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { readFileSync } from 'fs';
1212
import { fileURLToPath } from 'url';
1313
import { dirname, join } from 'path';
1414
import * as storage from '../storage/filesystem.js';
15+
import { generatePublicReadAcl, serializeAcl } from '../wac/parser.js';
1516

1617
const __dirname = dirname(fileURLToPath(import.meta.url));
1718
const TEMPLATE_PATH = join(__dirname, 'server-root.html');
@@ -96,25 +97,6 @@ function escape(s = '') {
9697
.replace(/"/g, '"');
9798
}
9899

99-
/**
100-
* Build a public-read-only ACL (no owner) for the server root.
101-
* The existing WAC generators all require an owner WebID; the server
102-
* root has none, so this is a tiny local helper.
103-
*/
104-
function publicReadAcl(target) {
105-
return JSON.stringify({
106-
'@context': { acl: 'http://www.w3.org/ns/auth/acl#', foaf: 'http://xmlns.com/foaf/0.1/' },
107-
'@graph': [
108-
{
109-
'@id': '#public',
110-
'@type': 'acl:Authorization',
111-
'acl:agentClass': { '@id': 'foaf:Agent' },
112-
'acl:accessTo': { '@id': target },
113-
'acl:mode': [{ '@id': 'acl:Read' }]
114-
}
115-
]
116-
}, null, 2);
117-
}
118100

119101
/**
120102
* Seed DATA_ROOT/index.html, DATA_ROOT/.acl and DATA_ROOT/index.html.acl
@@ -152,15 +134,15 @@ export async function seedServerRoot(ctx = {}) {
152134
// (createRootPodStructure in single-user mode writes its own ACL and
153135
// runs in a later hook, which will overwrite this if needed.)
154136
if (!(await storage.exists('/.acl'))) {
155-
const ok = await storage.write('/.acl', publicReadAcl('/'));
137+
const ok = await storage.write('/.acl', serializeAcl(generatePublicReadAcl('/')));
156138
if (ok) seededAcl = true;
157139
}
158140

159141
// Dedicated ACL for the landing page itself — public read. The container
160142
// ACL above has no acl:default (we don't want to implicitly publish all
161143
// children), so /index.html needs its own rule when fetched directly.
162144
if (!(await storage.exists('/index.html.acl'))) {
163-
const ok = await storage.write('/index.html.acl', publicReadAcl('/index.html'));
145+
const ok = await storage.write('/index.html.acl', serializeAcl(generatePublicReadAcl('/index.html')));
164146
if (ok) seededPageAcl = true;
165147
}
166148

0 commit comments

Comments
 (0)