Index: scripts/export-archive.mjs
===================================================================
--- scripts/export-archive.mjs	(revision 952baf39d4219c186a0428025fb689aec377f7f5)
+++ scripts/export-archive.mjs	(revision ed5e7aba95dbf4baef623c8f12357708916a53e2)
@@ -6,4 +6,13 @@
 //     node scripts/export-archive.mjs <slug> --out boiert.zip
 //     node scripts/export-archive.mjs <slug> --dir ./archief
+//
+// Op een machine met meerdere instances leest hij de .env van die instance zelf
+// (standaard /var/lib/klonkt/<slug>/.env). Dat is niet netjesheid maar noodzaak:
+// zonder die .env opent hij de database die toevallig in de code-map ligt, en op
+// een server die ooit de enkelvoudige opzet draaide is dat een oude lege. Dan
+// komt er een archief uit dat MELDT dat het gelukt is en nul posts bevat.
+//
+//     --data-root <map>   waar de instances staan (standaard /var/lib/klonkt)
+//     --env <pad>         een .env rechtstreeks aanwijzen
 //
 // Het formaat staat in docs/EXPORT-FORMAT.md.
@@ -15,18 +24,63 @@
 import fs from 'fs';
 import path from 'path';
-import { buildArchive, zipArchive, writeArchiveDir } from '../src/services/ArchiveExportService.js';
+import { kiesInstance, eisOrigin, splitsArgs } from './instance-env.mjs';
 
 const args = process.argv.slice(2);
-const slug = args.find((a) => !a.startsWith('-'));
-const vlag = (naam) => { const i = args.indexOf(naam); return i >= 0 ? (args[i + 1] || true) : null; };
+// Twee namen, en ze zijn niet hetzelfde. De eerste is de INSTANCE (de map onder
+// de data-root, de systemd-unit); de tweede, optioneel, is de slug van de SITE in
+// die database. Laat je hem weg en er staat er precies een, dan is de keuze niet
+// dubbelzinnig en hoef je hem niet te weten.
+const { vrij, vlaggen } = splitsArgs(args, ['--data-root', '--env', '--out', '--dir']);
+const instance = vrij[0];
+let slug = vrij[1] || null;
+const vlag = (naam) => vlaggen[naam] ?? null;
 
-if (!slug) {
-  console.error('gebruik: node scripts/export-archive.mjs <slug> [--dry-run | --out <zip> | --dir <map>]');
+if (!instance) {
+  console.error('gebruik: node scripts/export-archive.mjs <instance> [site-slug] [--data-root <map>] [--env <pad>] [--dry-run | --out <zip> | --dir <map>]');
   process.exit(1);
 }
 
-const uit = buildArchive(slug);
+// EERST de instance kiezen, DAN pas de service laden: src/config/database.js
+// leest DATABASE_PATH bij import en opent de database meteen.
+let gekozen;
+try {
+  gekozen = kiesInstance(instance, {
+    dataRoot: typeof vlag('--data-root') === 'string' ? vlag('--data-root') : null,
+    envPad: typeof vlag('--env') === 'string' ? vlag('--env') : null,
+  });
+} catch (e) { console.error(e.message); process.exit(1); }
+eisOrigin(gekozen.bron);
+
+const { buildArchive, zipArchive, writeArchiveDir } = await import('../src/services/ArchiveExportService.js');
+const { default: db } = await import('../src/config/database.js');
+
+// Geen slug gegeven? Dan mag de database het zeggen, mits het antwoord eenduidig is.
+if (!slug) {
+  let sites = [];
+  try { sites = db.prepare('SELECT slug FROM sites ORDER BY rowid').all().map((r) => r.slug); } catch { /* geen tabel */ }
+  if (sites.length === 1) {
+    [slug] = sites;
+    console.log(`site niet opgegeven, en er staat er precies een: ${slug}`);
+  } else if (sites.length === 0) {
+    console.error(`in ${process.env.DATABASE_PATH || 'deze database'} staat geen enkele site.`);
+    process.exit(1);
+  } else {
+    console.error(`meerdere sites in deze database: ${sites.join(', ')}\nGeef er een op: node scripts/export-archive.mjs ${instance} <site-slug>`);
+    process.exit(1);
+  }
+}
+
+let uit;
+try { uit = buildArchive(slug); }
+catch (e) {
+  console.error(`${e.message}`);
+  if (gekozen.bron) console.error(`(gelezen uit ${gekozen.bron})`);
+  else console.error('(geen instance-.env gevonden -- op een split install: --data-root /var/lib/klonkt)');
+  process.exit(1);
+}
 const t = uit.counts;
-console.log(`site      : ${uit.manifest.site.slug} (${uit.manifest.origin || 'geen PUBLIC_BASE_URL'})`);
+console.log(`site      : ${uit.manifest.site.slug} (${uit.manifest.origin})`);
+console.log(`instellingen uit: ${gekozen.bron || 'de omgeving'}`);
+console.log(`database  : ${process.env.DATABASE_PATH || '(standaard in de code-map)'}`);
 console.log(`posts     : ${t.posts}`);
 console.log(`antwoorden: ${t.replies}   (alleen-lezen archief)`);
Index: scripts/import-archive.mjs
===================================================================
--- scripts/import-archive.mjs	(revision 952baf39d4219c186a0428025fb689aec377f7f5)
+++ scripts/import-archive.mjs	(revision ed5e7aba95dbf4baef623c8f12357708916a53e2)
@@ -17,14 +17,28 @@
 // en hoort een aparte, bewuste actie te zijn.
 
-import { readArchive, importArchive } from '../src/services/ArchiveImportService.js';
+import { kiesInstance, eisOrigin, splitsArgs } from './instance-env.mjs';
 
 const args = process.argv.slice(2);
-const vrij = args.filter((a) => !a.startsWith('-'));
+const { vrij, vlaggen } = splitsArgs(args, ['--data-root', '--env']);
 const [slug, bron] = vrij;
 
 if (!slug || !bron) {
-  console.error('gebruik: node scripts/import-archive.mjs <slug> <archief.zip|map> [--dry-run] [--overwrite]');
+  console.error('gebruik: node scripts/import-archive.mjs <slug> <archief.zip|map> [--data-root <map>] [--env <pad>] [--dry-run] [--overwrite]');
   process.exit(1);
 }
+
+// EERST de instance kiezen, DAN pas de service laden -- database.js opent de
+// database bij import. Bij een IMPORT weegt dit zwaarder dan bij een export: in
+// de verkeerde database schrijven is niet terug te draaien.
+const vlag = (naam) => vlaggen[naam] ?? null;
+let gekozen;
+try {
+  gekozen = kiesInstance(slug, { dataRoot: vlag('--data-root'), envPad: vlag('--env') });
+} catch (e) { console.error(e.message); process.exit(1); }
+eisOrigin(gekozen.bron);
+console.log(`instellingen uit: ${gekozen.bron || 'de omgeving'}`);
+console.log(`database        : ${process.env.DATABASE_PATH || '(standaard in de code-map)'}`);
+
+const { readArchive, importArchive } = await import('../src/services/ArchiveImportService.js');
 
 let files;
Index: scripts/instance-env.mjs
===================================================================
--- scripts/instance-env.mjs	(revision ed5e7aba95dbf4baef623c8f12357708916a53e2)
+++ scripts/instance-env.mjs	(revision ed5e7aba95dbf4baef623c8f12357708916a53e2)
@@ -0,0 +1,118 @@
+//
+// Welke instance bedoel je?
+//
+// Op een split install (deploy/MULTI-INSTANCE.md) deelt elke Klonkt dezelfde code
+// in /opt/klonkt, maar staat zijn DATA onder /var/lib/klonkt/<slug>/ met een eigen
+// .env. Een script dat vanuit de code-map draait zonder die .env te lezen opent
+// dus de VERKEERDE database -- en op een machine die ooit de oude enkelvoudige
+// opzet draaide is dat een achtergebleven storage/database.sqlite in de checkout.
+//
+// Dat ging in de praktijk mis en op de ergste manier: het meldde SUCCES. Een
+// export van een site met zeven jaar aan posten schreef een archief van 444 bytes
+// met "posts: 0", omdat hij in een oude lege database keek. Een leeg archief dat
+// zegt dat het gelukt is, is erger dan een foutmelding.
+//
+// Vandaar dit: los, want de importer en het herstel hebben hem net zo hard nodig.
+
+import fs from 'fs';
+import path from 'path';
+
+/** Lees een .env zonder afhankelijkheden. Alleen KEY=value, # is commentaar. */
+export function leesEnv(pad) {
+  const uit = {};
+  for (const regel of fs.readFileSync(pad, 'utf8').split('\n')) {
+    const s = regel.trim();
+    if (!s || s.startsWith('#')) continue;
+    const i = s.indexOf('=');
+    if (i < 1) continue;
+    let v = s.slice(i + 1).trim();
+    if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
+    uit[s.slice(0, i).trim()] = v;
+  }
+  return uit;
+}
+
+/**
+ * Zet de omgeving voor EEN instance, en zeg hardop welke.
+ *
+ * Moet AANGEROEPEN WORDEN VOORDAT src/config/database.js geladen wordt: die leest
+ * DATABASE_PATH bij import en opent de database meteen. Vandaar dat de scripts
+ * hun service-import uitstellen tot na deze aanroep.
+ *
+ * @returns {{bron: string, env: object}} waar de instellingen vandaan kwamen
+ */
+export function kiesInstance(slug, opts = {}) {
+  const dataRoot = opts.dataRoot || process.env.KLONKT_DATA_ROOT || '/var/lib/klonkt';
+  const kandidaat = opts.envPad || path.join(dataRoot, slug, '.env');
+
+  if (fs.existsSync(kandidaat)) {
+    const env = leesEnv(kandidaat);
+    // De data-map van de instance is de wortel voor relatieve paden in zijn .env,
+    // precies zoals de unit hem draait (WorkingDirectory is de code, DATA_DIR de data).
+    const basis = env.DATA_DIR || path.dirname(kandidaat);
+    const absoluut = (p) => (p && !path.isAbsolute(p) ? path.resolve(basis, p) : p);
+    for (const k of ['DATABASE_PATH', 'MEDIA_PATH', 'AUDIO_PATH', 'AVATAR_PATH', 'COVER_PATH']) {
+      if (env[k]) process.env[k] = absoluut(env[k]);
+    }
+    if (env.PUBLIC_BASE_URL) process.env.PUBLIC_BASE_URL = env.PUBLIC_BASE_URL;
+    if (env.DATA_DIR) process.env.DATA_DIR = env.DATA_DIR;
+    return { bron: kandidaat, env };
+  }
+
+  if (opts.envPad) {
+    // Expliciet meegegeven en niet gevonden: dat is een vergissing en geen reden
+    // om stilletjes iets anders te openen.
+    throw new Error(`geen .env op ${opts.envPad}`);
+  }
+  return { bron: null, env: {} };
+}
+
+/**
+ * Weiger te beginnen als we niet kunnen weten wiens gegevens dit zijn.
+ *
+ * Zonder PUBLIC_BASE_URL komt er een archief uit met een lege `origin`, en dan
+ * weigert de importer de AP-ids te behouden -- hij maakt nieuwe. Daarmee valt
+ * precies de belofte weg waar het formaat om draait: een herstel waarna de
+ * boosts en antwoorden elders hun post terugvinden. Dat mag geen voetnoot in de
+ * uitvoer zijn.
+ */
+export function eisOrigin(bron) {
+  if (process.env.PUBLIC_BASE_URL) return;
+  console.error([
+    'GESTOPT: er is geen PUBLIC_BASE_URL.',
+    '',
+    'Zonder die waarde krijgt het archief een lege origin, en dan maakt een import',
+    'NIEUWE ActivityPub-ids in plaats van de oude te behouden. Boosts, likes en',
+    'antwoorden elders vinden hun post dan niet meer terug -- het archief is dan',
+    'een kopie van de tekst en geen herstel.',
+    '',
+    bron
+      ? `Gelezen uit ${bron}, maar PUBLIC_BASE_URL staat daar niet in.`
+      : 'Geen .env van een instance gevonden. Op een split install:\n'
+        + '  node scripts/export-archive.mjs <slug> --data-root /var/lib/klonkt',
+  ].join('\n'));
+  process.exit(1);
+}
+
+/**
+ * Argumenten splitsen in vrije woorden en vlaggen.
+ *
+ * Nodig omdat de naieve versie -- alles wat niet met een streepje begint is
+ * positioneel -- de WAARDE van een vlag als positioneel argument oppikt. Met
+ * `export-archive.mjs liz --data-root /var/lib/klonkt` werd /var/lib/klonkt de
+ * site-slug. Precies zo'n fout die pas opvalt als iemand hem gebruikt.
+ *
+ * @param {string[]} args
+ * @param {string[]} metWaarde  vlaggen die een waarde slikken
+ */
+export function splitsArgs(args, metWaarde) {
+  const vrij = [];
+  const vlaggen = {};
+  for (let i = 0; i < args.length; i += 1) {
+    const a = args[i];
+    if (!a.startsWith('-')) { vrij.push(a); continue; }
+    if (metWaarde.includes(a)) { vlaggen[a] = args[i + 1] ?? null; i += 1; continue; }
+    vlaggen[a] = true;
+  }
+  return { vrij, vlaggen };
+}
