diff --git a/scripts/build-site.mjs b/scripts/build-site.mjs
index b828261..f1cdc4e 100644
--- a/scripts/build-site.mjs
+++ b/scripts/build-site.mjs
@@ -483,6 +483,8 @@ ${cfg.faq.map((f) => `
${esc(f.q)}
${esc(f.a)}
# The human front door to the stack: the map, the principles, and how a protocol joins.
# No repository on the map is public or launched today; this site makes zero external requests.
+Source: ${cfg.links.repo}
+
## Pages
- ${canonicalFor('')} — the pitch and the nine-layer table, rendered from stack.json.
- ${canonicalFor('stack')} — every layer, its question, its repositories and its status.
@@ -510,7 +512,7 @@ The machine twin is maintained at ${cfg.links.spec}; this repository is ${cfg.li
// from, plus this org's directory/1 node — all from one source
// (flashyos.roles.json), so they cannot disagree. "Committed is not served":
// the authoritative check is `npx @flashyos/conformance
--level 2`.
- put(join('.well-known', 'flashyos.json'), handshakeJson());
+ put(join('.well-known', 'flashyos.json'), handshakeJson(undefined, { source: cfg.links.repo }));
put(join('.well-known', 'flashyos-charter.json'), charterJson());
written.push('.well-known/flashyos.json', '.well-known/flashyos-charter.json');
diff --git a/scripts/mesh.mjs b/scripts/mesh.mjs
index e433e4d..1494438 100644
--- a/scripts/mesh.mjs
+++ b/scripts/mesh.mjs
@@ -92,7 +92,7 @@ export function capabilitiesOf(charter) {
* the two surfaces cannot disagree. `apiBase`/`webBase`/`appBase` default to the
* estate's hosts; a caller may override for a test.
*/
-export function handshakeFromCharter(charter, { apiBase = 'https://api.flashyos.com', webBase = 'https://flashyos.com', appBase = 'https://app.flashyos.com' } = {}) {
+export function handshakeFromCharter(charter, { apiBase = 'https://api.flashyos.com', webBase = 'https://flashyos.com', appBase = 'https://app.flashyos.com', source = null } = {}) {
return {
mesh: MESH,
org: { slug: charter.slug, name: charter.name, profile: `${appBase}/org/${charter.slug}` },
@@ -100,6 +100,12 @@ export function handshakeFromCharter(charter, { apiBase = 'https://api.flashyos.
wants: `${apiBase}/api/v1/network/roadmap`,
api: apiBase,
join: `${webBase}/join`,
+ // Where this document's source lives — a convention the v1 contract
+ // ignores, read by flashyos's estate-source survey against the register.
+ // The value is `links.repo` from site.config.json, the one place this
+ // repository already declares itself; absent, no field is emitted rather
+ // than a guess.
+ ...(source ? { 'x-source': source } : {}),
};
}
@@ -109,6 +115,6 @@ export function charterJson(charter = validateCharter(loadCharter())) {
}
/** The bytes served at /.well-known/flashyos.json (the derived handshake). */
-export function handshakeJson(charter = validateCharter(loadCharter())) {
- return JSON.stringify(handshakeFromCharter(charter), null, 2) + '\n';
+export function handshakeJson(charter = validateCharter(loadCharter()), options = {}) {
+ return JSON.stringify(handshakeFromCharter(charter, options), null, 2) + '\n';
}
diff --git a/site/.well-known/flashyos.json b/site/.well-known/flashyos.json
index 1d85889..8688c75 100644
--- a/site/.well-known/flashyos.json
+++ b/site/.well-known/flashyos.json
@@ -10,5 +10,6 @@
],
"wants": "https://api.flashyos.com/api/v1/network/roadmap",
"api": "https://api.flashyos.com",
- "join": "https://flashyos.com/join"
+ "join": "https://flashyos.com/join",
+ "x-source": "https://github.com/FlashyLabs/web4"
}
diff --git a/site/llms.txt b/site/llms.txt
index 25af89c..7bdde7c 100644
--- a/site/llms.txt
+++ b/site/llms.txt
@@ -3,6 +3,8 @@
# The human front door to the stack: the map, the principles, and how a protocol joins.
# No repository on the map is public or launched today; this site makes zero external requests.
+Source: https://github.com/FlashyLabs/web4
+
## Pages
- https://flashylabs.github.io/web4/ — the pitch and the nine-layer table, rendered from stack.json.
- https://flashylabs.github.io/web4/stack/ — every layer, its question, its repositories and its status.
diff --git a/test/mesh.test.mjs b/test/mesh.test.mjs
index a033854..5bce5a1 100644
--- a/test/mesh.test.mjs
+++ b/test/mesh.test.mjs
@@ -42,7 +42,10 @@ test('charterJson round-trips the charter exactly', () => {
});
test('the served mesh files are the derived bytes', () => {
- assert.equal(read('site/.well-known/flashyos.json'), handshakeJson());
+ // The build passes site.config.json's `links.repo` as the handshake's source
+ // (test/source.test.mjs holds that), so the derivation here does too.
+ const cfg = JSON.parse(read('site.config.json'));
+ assert.equal(read('site/.well-known/flashyos.json'), handshakeJson(undefined, { source: cfg.links.repo }));
assert.equal(read('site/.well-known/flashyos-charter.json'), charterJson());
});
diff --git a/test/source.test.mjs b/test/source.test.mjs
new file mode 100644
index 0000000..d82597b
--- /dev/null
+++ b/test/source.test.mjs
@@ -0,0 +1,39 @@
+// The served handshake and llms.txt say where this repository's source lives,
+// and they say what site.config.json's `links.repo` says — the one place this
+// repository already declares itself. The register comparison runs only when
+// flashyos is checked out beside this repository (unknown, never passed, when
+// it is not). This site carries no Organization JSON-LD, so there is no
+// `sameAs` to hold; the FAQPage node is the only structured data it emits.
+import { test } from 'node:test';
+import assert from 'node:assert/strict';
+import { existsSync, readFileSync } from 'node:fs';
+import { join, dirname } from 'node:path';
+import { fileURLToPath } from 'node:url';
+import { handshakeFromCharter, loadCharter, validateCharter } from '../scripts/mesh.mjs';
+
+const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
+const cfg = JSON.parse(readFileSync(join(ROOT, 'site.config.json'), 'utf8'));
+
+test('the built handshake carries x-source from links.repo', () => {
+ const doc = JSON.parse(readFileSync(join(ROOT, 'site', '.well-known', 'flashyos.json'), 'utf8'));
+ assert.equal(doc['x-source'], cfg.links.repo);
+});
+
+test('the built llms.txt carries a Source: line from links.repo', () => {
+ const line = readFileSync(join(ROOT, 'site', 'llms.txt'), 'utf8').split('\n').find((l) => l.startsWith('Source: '));
+ assert.equal(line, `Source: ${cfg.links.repo}`);
+});
+
+test('a handshake derived without a source carries no x-source rather than a guess', () => {
+ const charter = validateCharter(loadCharter());
+ assert.equal('x-source' in handshakeFromCharter(charter), false);
+ assert.equal(handshakeFromCharter(charter, { source: 'https://example.invalid/r' })['x-source'], 'https://example.invalid/r');
+});
+
+test('agrees with the estate register when flashyos is checked out beside this repository', (t) => {
+ const p = join(ROOT, '..', 'flashyos', 'estate.register.json');
+ if (!existsSync(p)) { t.diagnostic('UNKNOWN: no flashyos checkout beside this repository — the register was not compared'); return; }
+ const row = JSON.parse(readFileSync(p, 'utf8')).rows.find((r) => r.slug === 'web4');
+ if (!row) { t.diagnostic('UNKNOWN: the sibling flashyos checkout has no web4 row on its current branch'); return; }
+ assert.equal(cfg.links.repo, row.url);
+});