IsotopeAI / docs

Guides

Getting started Configuration Supabase setup Sync & backup Backup console Community

Reference

Architecture API reference Database CLI Android APK Admin console

Help

Troubleshooting FAQ Contributing Changelog

Legal

Security Privacy Terms License

Contributing

Repository layout, the CI gates a change must pass, and how to verify work locally before opening a pull request.

Repository layout #

PathWhat it is
server.mjsThe entire server. ~10,000 lines: config, script injection, serve-time bundle patching, every route, startup backfills.
index.htmlThe SPA shell. At the repository root, not in public/.
public/assets/Pre-built minified Vite output. Patched at request time, never modified on disk.
public/sync/Sync engine modules, including backup-normalizer.js.
public/*.jsRuntime glue: auth bridge, boot recovery, restore-and-launch, update checker.
bin/isotopeThe CLI, plus .bat and .ps1 equivalents.
scripts/*.mjsOperational scripts. No test framework; each is standalone Node.
sql/Numbered migrations and the portable schema dump.
docs/This site. Plain HTML and CSS, no build step.
src/Vite/React scaffold. Not what is served — editing it has no effect on users.

src/ is not the running appThe React source in src/ is a shadcn scaffold. What users receive is the pre-built bundle set in public/assets/, rewritten by server.mjs at request time. Changing a feature usually means adding a serve-time patch, not editing src/.

Zero dependencies is a constraint, not an accident #

package.json lists no dependencies and no devDependencies. Please keep it that way. It is why install works on a phone with no build toolchain, and why there is no transitive supply chain to audit.

Working with serve-time patches #

Each patched bundle has a getPatched*Bundle() function that reads the file, performs exact string replacements against minified code, memoises the result and returns a Buffer.

const ANCHOR_FROM = 'exact minified substring from the built bundle';
const ANCHOR_TO   = 'replacement';

if (raw.includes(ANCHOR_FROM)) {
  raw = raw.replace(ANCHOR_FROM, ANCHOR_TO);
  console.log('[MyPatch] applied');
} else {
  console.warn('[MyPatch] anchor not found');
  _criticalPatchFailures.push('my-patch');   // surfaces as a UI banner
}

Three rules when adding one:

CI gates #

13 workflows. These are the ones that will block a pull request:

WorkflowGate
ci.ymlbash -n on 9 shell scripts, node --check on 20 JS files including sw.js both raw and with placeholders substituted, a backup-normalizer assertion, required-files presence, a secret scan, and a server smoke test on port 3099.
schema-lint.ymlExecutes isotope-complete.sql against Postgres 16 with a Supabase compatibility shim. Zero ERROR lines permitted. Also validates root-level patch SQL and dollar-quote balance.
codeql.ymlStatic analysis on JavaScript and TypeScript.
pages.ymlDeploys docs/ to GitHub Pages.
release.ymlBuilds and verifies the release archive.

The two drift checkers #

Both exist because a real bug got past review, and both fail loudly rather than warning.

# does isotope-complete.sql still match the live schema?
npm run schema:drift

# are all dollar-quoted SQL bodies balanced?
npm run sql:quotes

Why the dollar-quote checker existsA stray $$; in isotope-complete.sql silently swallowed the next CREATE FUNCTION header. Postgres then reported a confusing syntax error thousands of lines later. The checker matches tags properly — $$, $fn$, $iso_fn$ — and balances each independently.

Verifying a change locally #

# 1 — syntax
node --check server.mjs
node --check public/sw.js
bash -n bin/isotope

# 2 — restart and confirm every patch anchor still matches
bash bin/isotope restart && sleep 6
grep -iE 'anchor not found|String not found' ~/.isotope/logs/server.log   # want no output

# 3 — every patched bundle must return 200 and parse
for f in Community-CEnEgsrd useAuthStore-Aw1au7RF index-D1Y5F8Lk; do
  curl -s "http://127.0.0.1:3000/assets/$f.js" -o /tmp/b.js
  node --check /tmp/b.js || echo "PARSE FAIL $f"
done

# 4 — test suite
npm run test:auth-bridge
npm run test:runtime-glue
npm run docs:validate

Editing these docs #

Plain HTML in docs/, one stylesheet, one script, no build step. Pages share the chrome verbatim — if you change the topbar, drawer, sidebar or footer, change it in every page or navigation drifts.

Conventions:

npm run docs:validate

Commits #

Conventional prefixes — fix:, feat:, chore:, docs:. The body matters more than the subject: state what was wrong, why it was wrong, and how the fix addresses it. A future reader needs the reasoning, not a restatement of the diff.

Known open items #

ItemDetail
community_bootstrap_profile cannot runIts UPDATE assigns profile_data four times in one statement, which PostgreSQL rejects with 42601: multiple assignments to same column. It is the only writer to user_profiles, so that table stays empty and every buddy feature fails downstream.
Buddy handle is written and read in different placesBootstrap writes the handle into profile_data->>'community_handle'; community_request_buddy reads the user_profiles.handle column. Even with 42601 fixed, buddy requests would still raise user_not_found.
Two search_path conventionsSome definer functions pin the empty string, the rest pin public. Both are safe; the inconsistency is not ideal.