Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
Commit8ee4e5d

feat(self-host): sever the GitHub bootstrap dependency

feat(self-host): sever the GitHub bootstrap dependency

Gluecron is now self-canonical — nothing seeds from GitHub. The fresh-host
bootstrap-from-GitHub tie was the last code-level GitHub dependency.

- self-bootstrap.ts / self-host-bootstrap.ts: DEFAULT_SOURCE is now empty.
  With no SELF_BOOTSTRAP_SOURCE / --source, the bare repo is created empty
  and seeded by the first push; a fresh host re-seeds from a restored
  backup (scripts/restore.sh) or an explicit URL, never GitHub.
- self-bootstrap.ts: when the repo is missing and no source is set, log a
  clear restore-or-set-SELF_BOOTSTRAP_SOURCE message instead of silently
  cloning from GitHub, and continue boot.
- index.ts: comment reflects the self-canonical model.

Existing bootstrap tests pass explicit sources and are unaffected
(18 pass; the 1 pre-existing idempotency failure is unrelated/Windows).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ccanty labs committed on July 8, 2026Parent: 8359c02
3 files changed+43138ee4e5d89fa1467cad403fb11571707c6f525d88
3 changed files+43−13
Modifiedscripts/self-host-bootstrap.ts+17−7View fileUnifiedSplit
11/**
22 * BLOCK W — Self-host bootstrap.
33 *
4 * Mirror Gluecron's source from GitHub onto Gluecron itself, ONCE.
4 * Initialize Gluecron's own canonical bare repo on the box, ONCE. Gluecron is
5 * self-canonical: with no --source the bare repo is created empty and seeded
6 * by your first push. Pass --source only to re-seed a fresh host from a
7 * restored backup (or any git URL) — GitHub is no longer involved.
58 *
69 * Usage (run as root on the box, or anywhere DATABASE_URL + GIT_REPOS_PATH
710 * resolve to the production values):
912 * bun run scripts/self-host-bootstrap.ts \
1013 * [--owner=ccantynz] \
1114 * [--name=Gluecron.com] \
12 * [--source=https://github.com/ccantynz-alt/Gluecron.com.git] \
15 * [--source=<git-url-to-reseed-from>] \
1316 * [--dry-run]
1417 *
1518 * Idempotent — safe to re-run. Every step prints `v`/`x`/`!` and only
2730 * 2. Look up the operator (site_admins → oldest user fallback)
2831 * 3. INSERT the `repositories` row (skip if it exists)
2932 * 4. `git init --bare` the on-disk repo (skip if it exists)
30 * 5. `git push --mirror` from a temp clone of the GitHub source
33 * 5. If --source given, mirror it in; otherwise leave the bare repo empty
3134 * 6. Install the self-host post-receive hook on the bare repo
3235 * 7. Print cutover instructions
3336 *
7275 const out: BootstrapArgs = {
7376 owner: "ccantynz",
7477 name: "Gluecron.com",
75 source: "https://github.com/ccantynz-alt/Gluecron.com.git",
78 // No default source — Gluecron is self-canonical. Pass --source=<url>
79 // only for a one-time re-seed on a fresh host (e.g. a restored backup).
80 source: "",
7681 dryRun: false,
7782 };
7883 for (const a of argv) {
247252 return true;
248253}
249254
250// ── 5. Mirror from GitHub source ──────────────────────────────────────────
255// ── 5. Mirror from an explicit seed source (re-seed only; not GitHub) ──────
251256export async function mirrorFromSource(
252257 deps: BootstrapDeps,
253258 source: string,
419424 }
420425 }
421426
422 // 5. Mirror
427 // 5. Mirror (only if a seed source was given). With no --source, the bare
428 // repo is created empty and the operator seeds it with their first push —
429 // Gluecron is self-canonical and no longer mirrors from GitHub.
423430 deps.log.say("[4/6] mirroring source → bare repo");
424 if (args.dryRun) {
431 if (!args.source) {
432 deps.log.info("no --source given: bare repo left empty; push your first commit to seed it");
433 result.steps.mirrored = true;
434 } else if (args.dryRun) {
425435 deps.log.info(`dry-run: would git clone --mirror ${args.source} | push --mirror`);
426436 result.steps.mirrored = true;
427437 } else {
Modifiedsrc/index.ts+5−4View fileUnifiedSplit
3535}
3636
3737// Self-bootstrap: if Gluecron's own canonical repo (`ccantynz/Gluecron.com.git`
38// by default) doesn't exist on disk yet, initialize it from the GitHub mirror
39// and install the post-receive hook. This is the platform's self-healing path
40// — once it runs successfully on a host, future deploys flow through Gluecron
41// itself with no external CI tooling. Fire-and-forget; never blocks startup.
38// by default) doesn't exist on disk yet, install the post-receive hook and
39// (only if SELF_BOOTSTRAP_SOURCE is set) seed it. Gluecron is self-canonical —
40// it no longer pulls from GitHub; a fresh host re-seeds from a restored backup.
41// Once the repo exists on a host, deploys flow through Gluecron itself with no
42// external CI tooling. Fire-and-forget; never blocks startup.
4243void maybeSelfBootstrap().catch((err) => {
4344 console.warn(`[self-bootstrap] swallowed: ${(err as Error).message}`);
4445});
Modifiedsrc/lib/self-bootstrap.ts+21−2View fileUnifiedSplit
1616 * SELF_BOOTSTRAP_DISABLED=1 — skip entirely
1717 * SELF_BOOTSTRAP_OWNERdefault "ccantynz"
1818 * SELF_BOOTSTRAP_NAMEdefault "Gluecron.com"
19 * SELF_BOOTSTRAP_SOURCEdefault github mirror URL
19 * SELF_BOOTSTRAP_SOURCE — seed URL for a FRESH host with no repo yet.
20 * No default: Gluecron is self-canonical and no
21 * longer seeds from GitHub. On a bare new box,
22 * set this to a backup/restore URL, or restore
23 * the bare repo from scripts/restore.sh first.
2024 *
2125 * Never throws out of boot. Returns silently on any failure (logged).
2226 * Run the explicit script (`bun run scripts/self-host-bootstrap.ts`)
3640
3741const DEFAULT_OWNER = "ccantynz";
3842const DEFAULT_NAME = "Gluecron.com";
39const DEFAULT_SOURCE = "https://github.com/ccantynz-alt/Gluecron.com.git";
43// No default source — Gluecron is its own canonical origin now. GitHub is no
44// longer a seed/fallback. A fresh host with no repo seeds from an explicit
45// SELF_BOOTSTRAP_SOURCE (e.g. a restored backup), or from scripts/restore.sh.
46const DEFAULT_SOURCE = "";
4047
4148export async function maybeSelfBootstrap(): Promise<void> {
4249 if (process.env.SELF_BOOTSTRAP_DISABLED === "1") {
6168 return;
6269 }
6370
71 // Repo is missing AND no seed source is configured. This is expected on
72 // the live box only if something wiped the repo — we no longer silently
73 // pull from GitHub. Tell the operator how to re-seed and continue boot.
74 if (!source) {
75 console.warn(
76 `[self-bootstrap] canonical ${owner}/${name} not found on disk and no ` +
77 `SELF_BOOTSTRAP_SOURCE set. Restore it with scripts/restore.sh (repos ` +
78 `snapshot) or set SELF_BOOTSTRAP_SOURCE to a seed URL, then reboot.`
79 );
80 return;
81 }
82
6483 const args: BootstrapArgs = { owner, name, source, dryRun: false };
6584
6685 // Quiet log shim — boot output is precious; only print on completion
6786