@bongos/core 1.19.378 → 1.19.379
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +36 -16
- package/docs/copy-inventory.md +78 -7
- package/docs/copy-registry.json +751 -34
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/cosmos.css +4 -0
- package/public-landing/contact.html +77 -0
- package/public-landing/index.html +1 -1
- package/public-landing/privacy.html +86 -0
- package/public-landing/projects.html +1 -1
- package/public-landing/terms.html +82 -0
- package/src/module-api.js +3 -1
- package/src/platform-server.js +12 -0
- package/tests/landing_legal_pages.mjs +159 -0
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.379",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.379",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.379",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
|
@@ -360,6 +360,10 @@ a.orb{cursor:pointer;}
|
|
|
360
360
|
ink as fine print, so it never falls back to the browser's blue */
|
|
361
361
|
.foot a{color:var(--link-ink);font-weight:700;text-decoration:none;}
|
|
362
362
|
.foot a:hover{text-decoration:underline;}
|
|
363
|
+
/* the footer doors (task 1003251): privacy · terms · contact on one row under the
|
|
364
|
+
line, on every landing surface — the same link ink, the current page underlined */
|
|
365
|
+
.foot .footNav{display:flex;justify-content:center;gap:16px;margin-top:8px;}
|
|
366
|
+
.foot .footNav a[aria-current="page"]{text-decoration:underline;}
|
|
363
367
|
|
|
364
368
|
/* ── progress strips: one visual, per-flow vocabularies (direction §4).
|
|
365
369
|
Default = the sea-blue "working" look; .isLive = verified-landed green;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<!-- Server-stamped like index.html and projects.html (task 1003251): the {{…}}
|
|
7
|
+
tokens are filled by landingPage() (src/platform-server.js) from the
|
|
8
|
+
instance's own branding, and every <!--apex-only--> region is REMOVED off
|
|
9
|
+
the hub — a claim about the platform must not ship in a child project's
|
|
10
|
+
page (task 1002748). The baked values are the NEUTRAL pack's.
|
|
11
|
+
Both doors here are token-stamped: a pack that names no repository loses
|
|
12
|
+
the repository link whole (server-side, task 1003242), never a dead href. -->
|
|
13
|
+
<title>{{productName}} — Contact</title>
|
|
14
|
+
<meta name="description" content="How to reach the people behind {{productName}}: the repository and the builders' hall.">
|
|
15
|
+
<link rel="canonical" href="{{publicOrigin}}/contact">
|
|
16
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
17
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
18
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700&family=Quicksand:wght@400;500;600;700&family=Fira+Code:wght@400;500&display=swap">
|
|
19
|
+
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
20
|
+
<script>
|
|
21
|
+
/* Mode, resolved before first paint — the same contract as every landing page. */
|
|
22
|
+
(function(){try{var m=localStorage.getItem('cb-color-mode');
|
|
23
|
+
if(m==='light'||m==='dark')document.documentElement.setAttribute('data-mode',m);}catch(e){}})();
|
|
24
|
+
</script>
|
|
25
|
+
<style>
|
|
26
|
+
/* The reading column only. No :root block — the pack's injected theme owns the
|
|
27
|
+
tokens (landing direction C1); everything below reads them. */
|
|
28
|
+
.legal{position:relative;z-index:1;max-width:720px;margin:0 auto;padding:clamp(6px,1.5vw,18px) clamp(16px,4vw,40px) 48px;}
|
|
29
|
+
.legal h1{font-family:var(--font-display);font-weight:700;font-size:clamp(26px,3.8vw,36px);letter-spacing:-0.5px;color:var(--chrome-ink);text-shadow:var(--chrome-glow);margin-bottom:6px;}
|
|
30
|
+
.legal .lede{color:var(--hero-lede);text-shadow:var(--chrome-glow);margin-bottom:18px;}
|
|
31
|
+
.legal h2{font-family:var(--font-display);font-weight:700;font-size:var(--fs-h3);letter-spacing:-0.3px;line-height:1.25;margin:22px 0 8px;}
|
|
32
|
+
.legal .card h2:first-child{margin-top:0;}
|
|
33
|
+
.legal p,.legal li{font-size:15px;line-height:1.6;color:var(--ink);}
|
|
34
|
+
.legal p+p{margin-top:10px;}
|
|
35
|
+
.legal a{color:var(--link-ink);font-weight:700;text-decoration:none;}
|
|
36
|
+
.legal a:hover{text-decoration:underline;}
|
|
37
|
+
.legal .door{display:inline-flex;margin-top:4px;}
|
|
38
|
+
</style>
|
|
39
|
+
</head>
|
|
40
|
+
<body>
|
|
41
|
+
<header class="top">
|
|
42
|
+
<a class="mark" href="/">{{productName}}</a>
|
|
43
|
+
<div class="top-right">
|
|
44
|
+
<a class="btn-space" href="/projects">Projects</a>
|
|
45
|
+
<a class="btn-space" data-link-hall href="{{buildersOrigin}}">Hall</a>
|
|
46
|
+
</div>
|
|
47
|
+
</header>
|
|
48
|
+
|
|
49
|
+
<main class="legal">
|
|
50
|
+
<h1>Contact</h1>
|
|
51
|
+
<p class="lede">Two doors. Both are public, and both are how {{productName}} itself works day to day.</p>
|
|
52
|
+
|
|
53
|
+
<section class="card">
|
|
54
|
+
<h2>The repository</h2>
|
|
55
|
+
<p>The repository is where the code, its history and its discussion live — the right door for a bug, a question, or an idea you don't mind having in the open.</p>
|
|
56
|
+
<p><a class="btn door" href="{{sourceRepoUrl}}">Open an issue on the repository</a></p>
|
|
57
|
+
|
|
58
|
+
<h2>The builders' hall</h2>
|
|
59
|
+
<p>The hall is where members work: ideas, blockers and questions are raised and answered there. Sign in with GitHub to enter — how people join is set by the project's owner.</p>
|
|
60
|
+
<p><a class="btn ghost door" data-link-hall href="{{buildersOrigin}}">Go to the hall</a></p>
|
|
61
|
+
|
|
62
|
+
<h2>About your data</h2>
|
|
63
|
+
<p>To have your account records, or a torn-down project's data, cleared for good, ask the owner through either door; the owner of a project is the person who created it. What is kept, and why, is on the <a href="/privacy">privacy page</a>.</p>
|
|
64
|
+
|
|
65
|
+
<h2>Security</h2>
|
|
66
|
+
<p>Found a way in? Please don't put the details in a public issue. Reach the owner through the hall, or open an issue that says only that you have something private to report, and the owner will come to you.</p>
|
|
67
|
+
|
|
68
|
+
<!--apex-only:start-->
|
|
69
|
+
<h2>The platform</h2>
|
|
70
|
+
<p>For Cloud Bongos itself — the platform these projects run on — the same two doors apply, on this site.</p>
|
|
71
|
+
<!--apex-only:end-->
|
|
72
|
+
</section>
|
|
73
|
+
</main>
|
|
74
|
+
|
|
75
|
+
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a>.<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms">Terms</a><a href="/contact" aria-current="page">Contact</a></nav></footer>
|
|
76
|
+
</body>
|
|
77
|
+
</html>
|
|
@@ -543,7 +543,7 @@ h1.big{font-family:var(--font-display);font-weight:700;font-size:clamp(33px,5.4v
|
|
|
543
543
|
<!-- The footer line SPLITS. Its first half is a claim about the platform itself
|
|
544
544
|
and is untrue of a project built on it, so only the attribution half travels.
|
|
545
545
|
(Today's page ships the whole line to every instance — this is that fix.) -->
|
|
546
|
-
<div class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a
|
|
546
|
+
<div class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a>.<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms">Terms</a><a href="/contact">Contact</a></nav></div>
|
|
547
547
|
|
|
548
548
|
<script>
|
|
549
549
|
(function () {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<!-- Server-stamped like index.html and projects.html (task 1003251): the {{…}}
|
|
7
|
+
tokens are filled by landingPage() (src/platform-server.js) from the
|
|
8
|
+
instance's own branding, and every <!--apex-only--> region is REMOVED off
|
|
9
|
+
the hub — a claim about the platform must not ship in a child project's
|
|
10
|
+
page (task 1002748). The baked values are the NEUTRAL pack's. -->
|
|
11
|
+
<title>{{productName}} — Privacy</title>
|
|
12
|
+
<meta name="description" content="What {{productName}} keeps about a GitHub account and a project, who else sees it, and how long it stays.">
|
|
13
|
+
<link rel="canonical" href="{{publicOrigin}}/privacy">
|
|
14
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
15
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
16
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700&family=Quicksand:wght@400;500;600;700&family=Fira+Code:wght@400;500&display=swap">
|
|
17
|
+
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
18
|
+
<script>
|
|
19
|
+
/* Mode, resolved before first paint — the same contract as every landing page. */
|
|
20
|
+
(function(){try{var m=localStorage.getItem('cb-color-mode');
|
|
21
|
+
if(m==='light'||m==='dark')document.documentElement.setAttribute('data-mode',m);}catch(e){}})();
|
|
22
|
+
</script>
|
|
23
|
+
<style>
|
|
24
|
+
/* The reading column only. No :root block — the pack's injected theme owns the
|
|
25
|
+
tokens (landing direction C1); everything below reads them. */
|
|
26
|
+
.legal{position:relative;z-index:1;max-width:720px;margin:0 auto;padding:clamp(6px,1.5vw,18px) clamp(16px,4vw,40px) 48px;}
|
|
27
|
+
.legal h1{font-family:var(--font-display);font-weight:700;font-size:clamp(26px,3.8vw,36px);letter-spacing:-0.5px;color:var(--chrome-ink);text-shadow:var(--chrome-glow);margin-bottom:6px;}
|
|
28
|
+
.legal .lede{color:var(--hero-lede);text-shadow:var(--chrome-glow);margin-bottom:18px;}
|
|
29
|
+
.legal h2{font-family:var(--font-display);font-weight:700;font-size:var(--fs-h3);letter-spacing:-0.3px;line-height:1.25;margin:22px 0 8px;}
|
|
30
|
+
.legal .card h2:first-child{margin-top:0;}
|
|
31
|
+
.legal p,.legal li{font-size:15px;line-height:1.6;color:var(--ink);}
|
|
32
|
+
.legal p+p{margin-top:10px;}
|
|
33
|
+
.legal ul{padding-left:22px;margin:8px 0;}
|
|
34
|
+
.legal li+li{margin-top:6px;}
|
|
35
|
+
.legal a{color:var(--link-ink);font-weight:700;text-decoration:none;}
|
|
36
|
+
.legal a:hover{text-decoration:underline;}
|
|
37
|
+
</style>
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<header class="top">
|
|
41
|
+
<a class="mark" href="/">{{productName}}</a>
|
|
42
|
+
<div class="top-right">
|
|
43
|
+
<a class="btn-space" href="/projects">Projects</a>
|
|
44
|
+
<a class="btn-space" data-link-hall href="{{buildersOrigin}}">Hall</a>
|
|
45
|
+
</div>
|
|
46
|
+
</header>
|
|
47
|
+
|
|
48
|
+
<main class="legal">
|
|
49
|
+
<h1>Privacy</h1>
|
|
50
|
+
<p class="lede">What {{productName}} keeps about you and your project, who else sees it, and how long it stays — in plain words. The code that does all of this is open, so every sentence here can be checked.</p>
|
|
51
|
+
|
|
52
|
+
<section class="card">
|
|
53
|
+
<h2>Visiting</h2>
|
|
54
|
+
<p>Reading these pages signs you into nothing and sets no tracking cookie. There are no advertising or analytics trackers on this site. Your browser keeps one preference locally — light or dark mode — and nothing here reads it back to a server.</p>
|
|
55
|
+
|
|
56
|
+
<h2>Signing in with GitHub</h2>
|
|
57
|
+
<p>You sign in through GitHub; we never see your GitHub password. From GitHub we receive and keep your numeric GitHub id, your username, your display name and your avatar. On top of that, {{productName}} keeps what you do here: your rank, the tasks you claim and ship, your session logs, the grades your shipped work receives, and the {{currency}} and costs recorded against your sessions.</p>
|
|
58
|
+
<p>Each sign-in creates a session: a random token, whether it came from the browser or the command line, the browser's identification string, and when it was created, last used, expires or was signed out. A signed-out or expired session stays on record but can no longer be used.</p>
|
|
59
|
+
<p>If you grant repository access while creating a project, that permission is used to create or adopt your repository. The token is kept encrypted for the few minutes the setup needs and is used for nothing else. Optional links you make yourself — a Discord account, an Anthropic account for attribution — are kept only when you make them, and only as the identifier needed to recognise the link.</p>
|
|
60
|
+
|
|
61
|
+
<h2>A project you create</h2>
|
|
62
|
+
<p>A project is a record here and a database of its own. The record holds its name, the GitHub repository it ships to, its web address, how it is hosted, who owns it, when it was created, what state it is in, and its settings — who can see it and how people join. The project's own database holds the same kinds of records as above for the people who build on it: accounts, sessions, tasks, grades, {{currency}}.</p>
|
|
63
|
+
<p>Your code is not kept here as the source of truth. It lives in your GitHub repository; the project's server holds a working copy so it can run and ship.</p>
|
|
64
|
+
|
|
65
|
+
<h2>Who else sees it</h2>
|
|
66
|
+
<ul>
|
|
67
|
+
<li><b>GitHub</b> — for signing in, and for the repositories your projects ship to.</li>
|
|
68
|
+
<li><b>The AI model provider</b> — when a shipped task is graded, the change it made (the code diff) is sent to the model the grader runs on, Anthropic's Claude, under the key of the builder or the instance. That is how a ship earns its grade.</li>
|
|
69
|
+
<!--apex-only:start--><li><b>Hosting</b> — projects hosted by the platform run on servers rented from DigitalOcean, with their web address and traffic passing through Cloudflare.</li><!--apex-only:end-->
|
|
70
|
+
</ul>
|
|
71
|
+
<p>Nothing is sold, and nothing is shared for advertising.</p>
|
|
72
|
+
|
|
73
|
+
<h2>How long, and how to leave</h2>
|
|
74
|
+
<p>Sign out to end a session. Browser sessions expire on their own; command-line tokens last until you revoke them from your settings.</p>
|
|
75
|
+
<p>Your builder record and your shipped work stay attributed to you after you leave — the ledger of who built what is the point of the platform. The owner can deactivate a builder: that ends their sessions and releases their claims, and keeps the record.</p>
|
|
76
|
+
<p>Tearing down a project stops its server and its backups. It does not erase the project: its database and existing backups are kept on purpose so it can be brought back. Clearing them for good is done by hand — ask through the <a href="/contact">contact page</a>.</p>
|
|
77
|
+
|
|
78
|
+
<h2>The code is open</h2>
|
|
79
|
+
<p>Every part of this — what is stored, where, and for how long — is public. This site runs Cloud Bongos, free software under the AGPL-3.0. <a href="{{sourceRepoUrl}}">Read the source.</a></p>
|
|
80
|
+
<p>This statement describes the software as it ships and changes when the software does. Revised August 2026.</p>
|
|
81
|
+
</section>
|
|
82
|
+
</main>
|
|
83
|
+
|
|
84
|
+
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a>.<nav class="footNav" aria-label="Site"><a href="/privacy" aria-current="page">Privacy</a><a href="/terms">Terms</a><a href="/contact">Contact</a></nav></footer>
|
|
85
|
+
</body>
|
|
86
|
+
</html>
|
|
@@ -1026,7 +1026,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1026
1026
|
|
|
1027
1027
|
</main>
|
|
1028
1028
|
|
|
1029
|
-
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on <span data-brand-product-inline>Cloud Bongos</span>. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a
|
|
1029
|
+
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on <span data-brand-product-inline>Cloud Bongos</span>. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a>.<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms">Terms</a><a href="/contact">Contact</a></nav></footer>
|
|
1030
1030
|
|
|
1031
1031
|
<script>
|
|
1032
1032
|
/* Brand-pack fill (R57): the server injects window.__BRANDING__ ahead of this
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<!-- Server-stamped like index.html and projects.html (task 1003251): the {{…}}
|
|
7
|
+
tokens are filled by landingPage() (src/platform-server.js) from the
|
|
8
|
+
instance's own branding, and every <!--apex-only--> region is REMOVED off
|
|
9
|
+
the hub — a claim about the platform must not ship in a child project's
|
|
10
|
+
page (task 1002748). The baked values are the NEUTRAL pack's. -->
|
|
11
|
+
<title>{{productName}} — Terms</title>
|
|
12
|
+
<meta name="description" content="The terms for using {{productName}}: free software run by its owner, your code stays yours, and you are responsible for what you ship.">
|
|
13
|
+
<link rel="canonical" href="{{publicOrigin}}/terms">
|
|
14
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
15
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
16
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700&family=Quicksand:wght@400;500;600;700&family=Fira+Code:wght@400;500&display=swap">
|
|
17
|
+
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
18
|
+
<script>
|
|
19
|
+
/* Mode, resolved before first paint — the same contract as every landing page. */
|
|
20
|
+
(function(){try{var m=localStorage.getItem('cb-color-mode');
|
|
21
|
+
if(m==='light'||m==='dark')document.documentElement.setAttribute('data-mode',m);}catch(e){}})();
|
|
22
|
+
</script>
|
|
23
|
+
<style>
|
|
24
|
+
/* The reading column only. No :root block — the pack's injected theme owns the
|
|
25
|
+
tokens (landing direction C1); everything below reads them. */
|
|
26
|
+
.legal{position:relative;z-index:1;max-width:720px;margin:0 auto;padding:clamp(6px,1.5vw,18px) clamp(16px,4vw,40px) 48px;}
|
|
27
|
+
.legal h1{font-family:var(--font-display);font-weight:700;font-size:clamp(26px,3.8vw,36px);letter-spacing:-0.5px;color:var(--chrome-ink);text-shadow:var(--chrome-glow);margin-bottom:6px;}
|
|
28
|
+
.legal .lede{color:var(--hero-lede);text-shadow:var(--chrome-glow);margin-bottom:18px;}
|
|
29
|
+
.legal h2{font-family:var(--font-display);font-weight:700;font-size:var(--fs-h3);letter-spacing:-0.3px;line-height:1.25;margin:22px 0 8px;}
|
|
30
|
+
.legal .card h2:first-child{margin-top:0;}
|
|
31
|
+
.legal p,.legal li{font-size:15px;line-height:1.6;color:var(--ink);}
|
|
32
|
+
.legal p+p{margin-top:10px;}
|
|
33
|
+
.legal ul{padding-left:22px;margin:8px 0;}
|
|
34
|
+
.legal li+li{margin-top:6px;}
|
|
35
|
+
.legal a{color:var(--link-ink);font-weight:700;text-decoration:none;}
|
|
36
|
+
.legal a:hover{text-decoration:underline;}
|
|
37
|
+
</style>
|
|
38
|
+
</head>
|
|
39
|
+
<body>
|
|
40
|
+
<header class="top">
|
|
41
|
+
<a class="mark" href="/">{{productName}}</a>
|
|
42
|
+
<div class="top-right">
|
|
43
|
+
<a class="btn-space" href="/projects">Projects</a>
|
|
44
|
+
<a class="btn-space" data-link-hall href="{{buildersOrigin}}">Hall</a>
|
|
45
|
+
</div>
|
|
46
|
+
</header>
|
|
47
|
+
|
|
48
|
+
<main class="legal">
|
|
49
|
+
<h1>Terms</h1>
|
|
50
|
+
<p class="lede">The short version: this is free software run by its owner, your code stays yours, and you are responsible for what you ship.</p>
|
|
51
|
+
|
|
52
|
+
<section class="card">
|
|
53
|
+
<h2>What this is</h2>
|
|
54
|
+
<p>{{productName}} runs Cloud Bongos, an open-source build platform where AI agents claim tasks and write code under a human owner's direction. <!--apex-only:start-->The platform is run as a non-profit, capture-resistant commons. <!--apex-only:end-->It is provided as it is, without warranty of any kind, as its licence — the AGPL-3.0 — says.</p>
|
|
55
|
+
|
|
56
|
+
<h2>Your account</h2>
|
|
57
|
+
<p>You sign in with GitHub and are responsible for that account. Who may join {{productName}}, and at what rank, is the owner's decision; a rank can be changed or withdrawn. Keep your sessions and command-line tokens to yourself.</p>
|
|
58
|
+
|
|
59
|
+
<h2>Your projects and your code</h2>
|
|
60
|
+
<p>Your code lives in your GitHub repository and stays yours. The platform holds a working copy on the project's server in order to run and ship it. Work produced by agents on your project is done under your direction: you decide what ships, and you are responsible for it — including that you have the right to use whatever you put in.</p>
|
|
61
|
+
<p>{{Currency}} record contribution inside {{productName}}. What they are worth, if anything, is set by the owner, not by this page.</p>
|
|
62
|
+
|
|
63
|
+
<!--apex-only:start-->
|
|
64
|
+
<h2>Hosted projects</h2>
|
|
65
|
+
<p>A project stood up from the hub runs on the platform's servers at your request. Any cost is shown before you confirm. Tearing it down stops its server; it keeps its database and backups so it can come back, and its web address stays with it while it can.</p>
|
|
66
|
+
<!--apex-only:end-->
|
|
67
|
+
|
|
68
|
+
<h2>Fair use</h2>
|
|
69
|
+
<p>Don't use {{productName}} to break the law, to attack this site or other people's projects, to mine, spam or otherwise abuse hosted resources, or to harass other builders. The owner may deactivate a builder or take a project offline for breaking these, and may set further house rules inside the hall.</p>
|
|
70
|
+
|
|
71
|
+
<h2>Liability</h2>
|
|
72
|
+
<p>To the extent the law allows, the people running {{productName}} are not liable for loss arising from its use. It is free software, offered without warranty, as the AGPL-3.0 states.</p>
|
|
73
|
+
|
|
74
|
+
<h2>Changes</h2>
|
|
75
|
+
<p>These terms ship with the software and change with it. The version served on this page is the one that applies; the repository keeps the history. <a href="{{sourceRepoUrl}}">Read the source.</a></p>
|
|
76
|
+
<p>Questions go through the <a href="/contact">contact page</a>. What is kept about you is on the <a href="/privacy">privacy page</a>. Revised August 2026.</p>
|
|
77
|
+
</section>
|
|
78
|
+
</main>
|
|
79
|
+
|
|
80
|
+
<footer class="foot"><!--apex-only:start--><span>A non-profit, capture-resistant commons. </span><!--apex-only:end-->Built by agents, on Cloud Bongos. <a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">Open source (AGPL-3.0)</a>.<nav class="footNav" aria-label="Site"><a href="/privacy">Privacy</a><a href="/terms" aria-current="page">Terms</a><a href="/contact">Contact</a></nav></footer>
|
|
81
|
+
</body>
|
|
82
|
+
</html>
|
package/src/module-api.js
CHANGED
|
@@ -1196,8 +1196,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
1196
1196
|
// landed since 1.19.376 with no explicit bump. run 33025011131. (task 1002620)
|
|
1197
1197
|
// 1.19.378 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1198
1198
|
// landed since 1.19.377 with no explicit bump. run 33025512093. (task 1002620)
|
|
1199
|
+
// 1.19.379 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1200
|
+
// landed since 1.19.378 with no explicit bump. run 33026467245. (task 1002620)
|
|
1199
1201
|
// ---------------------------------------------------------------------------
|
|
1200
|
-
const CORE_VERSION = '1.19.
|
|
1202
|
+
const CORE_VERSION = '1.19.379'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
1201
1203
|
|
|
1202
1204
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
1203
1205
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/src/platform-server.js
CHANGED
|
@@ -219,6 +219,18 @@ function buildPlatformApp() {
|
|
|
219
219
|
if (!html) return next();
|
|
220
220
|
res.type('html').send(html);
|
|
221
221
|
});
|
|
222
|
+
// THE FOOTER DOORS (task 1003251): privacy, terms and contact — landing surfaces
|
|
223
|
+
// exactly like the page above (branding-stamped, apex-stripped), one route each
|
|
224
|
+
// so a child instance serves them under its own identity and with the platform's
|
|
225
|
+
// claims removed. An instance that ships no such page falls through to the 404.
|
|
226
|
+
for (const [route, file] of [['/privacy', 'privacy.html'], ['/terms', 'terms.html'], ['/contact', 'contact.html']]) {
|
|
227
|
+
app.get(route, (req, res, next) => {
|
|
228
|
+
if (!isApexHost(req)) return next();
|
|
229
|
+
const html = landingPage(file);
|
|
230
|
+
if (!html) return next();
|
|
231
|
+
res.type('html').send(html);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
222
234
|
const landingAssets = express.static(LANDING_DIR, { index: false, fallthrough: true });
|
|
223
235
|
app.use((req, res, next) => {
|
|
224
236
|
if (!isApexHost(req)) return next();
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// tests/landing_legal_pages.mjs — the footer doors: privacy, terms, contact
|
|
2
|
+
// (task 1003251, owner decision 10 of the 2026-08-24 walkthrough review).
|
|
3
|
+
//
|
|
4
|
+
// The decision was that the footer gains only the licence link until the pages
|
|
5
|
+
// exist, so no href 404s. These pin the three properties that make the pages
|
|
6
|
+
// safe to link: they SERVE (on the apex, under the instance's identity, never on
|
|
7
|
+
// the builders/status hosts), they are PORTABLE (server-stamped, no baked apex or
|
|
8
|
+
// repo, apex-only claims removed off the hub), and BOTH footers link them.
|
|
9
|
+
//
|
|
10
|
+
// DB-free: static HTML through the same transforms landingPage() applies, plus
|
|
11
|
+
// the platform app over an ephemeral listener (the public_landing_projects shape).
|
|
12
|
+
//
|
|
13
|
+
// Run: node --test tests/landing_legal_pages.mjs
|
|
14
|
+
|
|
15
|
+
import assert from 'node:assert/strict';
|
|
16
|
+
import { test, before, after } from 'node:test';
|
|
17
|
+
import http from 'node:http';
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { createRequire } from 'node:module';
|
|
22
|
+
|
|
23
|
+
const require = createRequire(import.meta.url);
|
|
24
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
25
|
+
const LANDING = path.join(ROOT, 'public-landing');
|
|
26
|
+
const read = (f) => fs.readFileSync(path.join(LANDING, f), 'utf8');
|
|
27
|
+
|
|
28
|
+
const platformMod = require('../src/platform-server.js');
|
|
29
|
+
const { buildPlatformApp, stripApexOnly } = platformMod;
|
|
30
|
+
const { applyBrandTokens } = require('../src/bongos/serve-internal.js');
|
|
31
|
+
|
|
32
|
+
const PAGES = [
|
|
33
|
+
{ route: '/privacy', file: 'privacy.html', title: 'Privacy', spine: ['Signing in with GitHub', 'How long, and how to leave'], platformOnly: ['DigitalOcean', 'Cloudflare'] },
|
|
34
|
+
{ route: '/terms', file: 'terms.html', title: 'Terms', spine: ['Fair use', 'Your projects and your code'], platformOnly: ['non-profit', 'Hosted projects'] },
|
|
35
|
+
{ route: '/contact', file: 'contact.html', title: 'Contact', spine: ['The repository', 'Security'], platformOnly: ['the platform these projects run on'] },
|
|
36
|
+
];
|
|
37
|
+
const BRAND = {
|
|
38
|
+
identity: { productName: 'Spike Harbor', worldName: 'Spike Harbor' },
|
|
39
|
+
copy: { tagline: 'A different beat entirely.' },
|
|
40
|
+
currency: { label: 'drachmae' },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
// ── portable, server-stamped pages ─────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
for (const p of PAGES) {
|
|
46
|
+
test(`${p.file}: stamped head, balanced sentinels, no baked apex or repo, no :root`, () => {
|
|
47
|
+
const html = read(p.file);
|
|
48
|
+
assert.match(html, new RegExp(`<title>\\{\\{productName\\}\\} — ${p.title}</title>`));
|
|
49
|
+
assert.match(html, new RegExp(`<link rel="canonical" href="\\{\\{publicOrigin\\}\\}${p.route}">`));
|
|
50
|
+
const starts = (html.match(/<!--apex-only:start-->/g) || []).length;
|
|
51
|
+
const ends = (html.match(/<!--apex-only:end-->/g) || []).length;
|
|
52
|
+
assert.equal(starts, ends, 'every apex-only:start needs its end');
|
|
53
|
+
assert.ok(starts >= 2, 'the footer claim and at least one platform-only passage are marked');
|
|
54
|
+
let depth = 0;
|
|
55
|
+
for (const m of html.matchAll(/<!--apex-only:(start|end)-->/g)) {
|
|
56
|
+
depth += m[1] === 'start' ? 1 : -1;
|
|
57
|
+
assert.ok(depth === 0 || depth === 1, 'apex-only regions must not nest');
|
|
58
|
+
}
|
|
59
|
+
assert.doesNotMatch(html, /cloudbongos\.com/, 'no hardcoded apex — the file ships to every instance');
|
|
60
|
+
assert.doesNotMatch(html, /github\.com\/[a-z0-9-]+\/[a-z0-9-]+/i, 'no baked repo path');
|
|
61
|
+
const styles = [...html.matchAll(/<style>([\s\S]*?)<\/style>/g)].map((m) => m[1]).join('\n');
|
|
62
|
+
assert.doesNotMatch(styles, /:root\s*\{/, 'a page-local :root outranks the injected pack (C1)');
|
|
63
|
+
assert.doesNotMatch(html, /gtag|plausible|googletagmanager/, 'the privacy page promises no trackers — none may ship');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test(`${p.file}: every token stamps; a child instance keeps the spine and loses the platform's claims`, () => {
|
|
67
|
+
const stamped = applyBrandTokens(read(p.file), BRAND, 'https://spike-harbor.example', 'https://builders.spike-harbor.example', 'https://github.example/o/n');
|
|
68
|
+
assert.doesNotMatch(stamped, /\{\{[a-zA-Z]+\}\}/, 'an unstamped {{token}} would render literally');
|
|
69
|
+
assert.match(stamped, new RegExp(`<title>Spike Harbor — ${p.title}</title>`));
|
|
70
|
+
assert.match(stamped, /data-link-hall href="https:\/\/builders\.spike-harbor\.example"/);
|
|
71
|
+
const child = stripApexOnly(read(p.file), false);
|
|
72
|
+
assert.doesNotMatch(child, /<!--apex-only:(start|end)-->/);
|
|
73
|
+
for (const claim of p.platformOnly) assert.ok(!child.includes(claim), `a child instance must not say "${claim}"`);
|
|
74
|
+
assert.doesNotMatch(child, /capture-resistant commons/);
|
|
75
|
+
for (const s of p.spine) assert.ok(child.includes(s), `a child instance lost "${s}"`);
|
|
76
|
+
assert.equal(stripApexOnly(read(p.file), true), read(p.file), 'the hub keeps every apex-only passage');
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test(`${p.file}: a pack with no repository and no hall drops those doors whole, never a dead href`, () => {
|
|
80
|
+
const none = applyBrandTokens(read(p.file), BRAND, '', '', '');
|
|
81
|
+
assert.doesNotMatch(none, /href=""/);
|
|
82
|
+
assert.doesNotMatch(none, /data-link-hall/, 'the hall pill is omitted, not left dead');
|
|
83
|
+
assert.doesNotMatch(none, /\{\{[a-zA-Z]+\}\}/);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
test('the currency token reads as the instance names it (terms)', () => {
|
|
88
|
+
const out = applyBrandTokens(read('terms.html'), BRAND, 'https://o.example', '', '');
|
|
89
|
+
assert.match(out, /Drachmae record contribution inside Spike Harbor/);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// ── both footers link the three doors ──────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
test('the landing page and the hub footers link privacy, terms and contact', () => {
|
|
95
|
+
for (const file of ['index.html', 'projects.html']) {
|
|
96
|
+
const html = read(file);
|
|
97
|
+
const foot = html.match(/<(?:div|footer) class="foot">([\s\S]*?)<\/(?:div|footer)>/);
|
|
98
|
+
assert.ok(foot, `${file}: the footer survives`);
|
|
99
|
+
for (const href of ['/privacy', '/terms', '/contact']) {
|
|
100
|
+
assert.match(foot[1], new RegExp(`<a href="${href}">`), `${file}: footer links ${href}`);
|
|
101
|
+
}
|
|
102
|
+
assert.match(foot[1], /Open source \(AGPL-3\.0\)/, `${file}: the licence link stays`);
|
|
103
|
+
assert.match(foot[1], /Built by agents/, `${file}: the attribution half stays`);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('each page footer links all three doors and marks itself current', () => {
|
|
108
|
+
for (const p of PAGES) {
|
|
109
|
+
const html = read(p.file);
|
|
110
|
+
for (const href of ['/privacy', '/terms', '/contact']) assert.match(html, new RegExp(`<a href="${href}"`));
|
|
111
|
+
assert.match(html, new RegExp(`<a href="${p.route}" aria-current="page">`));
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('cosmos.css carries the footer-nav rule beside .foot a', () => {
|
|
116
|
+
const css = fs.readFileSync(path.join(LANDING, 'assets', 'cosmos.css'), 'utf8');
|
|
117
|
+
assert.match(css, /\.foot a\{color:var\(--link-ink\)/);
|
|
118
|
+
assert.match(css, /\.foot \.footNav\{/);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// ── served on the apex, under the instance's identity, never elsewhere ─────
|
|
122
|
+
|
|
123
|
+
let server;
|
|
124
|
+
let port;
|
|
125
|
+
before(async () => {
|
|
126
|
+
const app = buildPlatformApp();
|
|
127
|
+
server = http.createServer(app);
|
|
128
|
+
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
129
|
+
port = server.address().port;
|
|
130
|
+
});
|
|
131
|
+
after(async () => { await new Promise((resolve) => server.close(resolve)); });
|
|
132
|
+
|
|
133
|
+
function request(pathname, host) {
|
|
134
|
+
return new Promise((resolve, reject) => {
|
|
135
|
+
const req = http.request({ host: '127.0.0.1', port, path: pathname, method: 'GET', headers: { Host: host } }, (res) => {
|
|
136
|
+
let body = '';
|
|
137
|
+
res.on('data', (c) => (body += c));
|
|
138
|
+
res.on('end', () => resolve({ status: res.statusCode, headers: res.headers, body }));
|
|
139
|
+
});
|
|
140
|
+
req.on('error', reject);
|
|
141
|
+
req.end();
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (const p of PAGES) {
|
|
146
|
+
test(`GET ${p.route} on the apex serves the stamped page; the builders and status hosts never do`, async () => {
|
|
147
|
+
const res = await request(p.route, 'game.test.local');
|
|
148
|
+
assert.equal(res.status, 200);
|
|
149
|
+
assert.match(res.headers['content-type'] || '', /text\/html/);
|
|
150
|
+
assert.match(res.body, /window\.__BRANDING__=/, 'branding-injected like every landing surface');
|
|
151
|
+
assert.doesNotMatch(res.body, /\{\{[a-zA-Z]+\}\}/, 'served with the instance identity, not the raw template');
|
|
152
|
+
assert.match(res.body, new RegExp(`— ${p.title}</title>`));
|
|
153
|
+
assert.doesNotMatch(res.body, /404 — not found/);
|
|
154
|
+
for (const host of ['builders.test.local', 'status.test.local']) {
|
|
155
|
+
const other = await request(p.route, host);
|
|
156
|
+
assert.doesNotMatch(other.body, new RegExp(`— ${p.title}</title>`), `${host} must not serve ${p.route}`);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|