Three new doors, all wire-contract ports of the shared @otm/account-panel factories (this backbone is plain Node — the Next factories can't mount): - GET /api/auth/operator-magic — OTM 'Log in as admin'. HS256 verify against OPERATOR_SHARED_SECRET (alg allowlist, constant-time, action-claim rejected for flow separation), single-use jti via OperatorMagicConsumed (+ who/when audit), 1-HOUR session with cookie Max-Age derived from the payload, 🔑 support-session banner in the app, every failure a 302 reason redirect. - GET /api/auth/otm-sso — the OTM /account tile. Stricter sso-ticket verify (exp mandatory, audience compared), email-then-role actor mapping, safeNext. - 💡 suggestion jot chip — files a Gitea issue (shared attribution footer, 8k cap, 10s timeout); on any failure the jot is kept as a note instead. Success also leaves a '#N — …' note so she has her own record. - Footer version chip + one-tap self-update (HMAC-signed OTM proxy with explicit field picking — foreign JSON can never reach the _setSession/_redirect control keys), owner-gated, 10-min sessionStorage cache on the check. Hardening that rode along: session key now derived from OPERATOR_SHARED_SECRET (managed boot REFUSES the old derivable DATABASE_URL fallback), 1MB JSON body cap, login.html prototype-lookup fix. NOTE: server.mjs previously contained a literal NUL byte that made git treat it as binary — this commit re-encodes it as an escape (behavior identical) and adds .gitattributes so source diffs can never go blind again. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SmybqyQmZWfcqA1vMP4jbQ
7.4 KiB
Bonna Moon Studio — access notes
Status as of 2026-08-06: provisioned and live at
https://bonnamoonstudio.poweredbyotm.com (site template v0.11.0, container
site-bonnamoonstudio on anvil). The repo now holds the studio-backbone
vision, Phase 1 designs, and a working local prototype — see
VISION.md and prototype/README.md. The
site instance's code still lives in the platform monorepo, not here (see "The
plan" below); this repo is the backbone's home. Local branch is now main,
tracking gitea/main.
The long-term product vision — a custom "studio backbone" app in the FMB mold
(glaze recipes, firing log, inventory, accounting, market scanner, kiln control) —
is captured in VISION.md. The site instance is the storefront layer.
The backbone is LIVE (2026-08-06): https://bms.poweredbyotm.com — template
bms-backbone-next (otm-admin 0.111.0), container bms-backbone-bms on anvil,
built from this repo's app/ (Postgres + Prisma port of the prototype, OTM
owner-seed auth). Deploy sequence: bump app/lib/version.mjs and
app/package.json + push here → the push webhook now AUTO-BUILDS the image
(autoBuildOnly, otm-admin 0.111.1) → deploying it stays a manual click on
OTM /instances (or redeploy_service via the MCP).
Bonna's laptop data was migrated; prototype/ is now legacy reference (the
laptop .command still works but the hosted notebook is the daily driver).
v0.4.0 — OTM integrations (what needs which env)
All three are provision-injected via the template's flags; a hand-provision that misses one degrades silently, so here's the lookup:
| Feature | Env it needs | Symptom when unset |
|---|---|---|
OTM "Log in as admin" (GET /api/auth/operator-magic) + OTM account SSO (GET /api/auth/otm-sso) |
OPERATOR_SHARED_SECRET; SSO also OTM_SSO_SECRET + OTM_SSO_AUDIENCE |
/login?reason=not_enabled |
| Suggestion jots → Gitea issues (the 💡 chip) | GITEA_URL + GITEA_TOKEN + GITEA_REPO |
jot saved as a note tagged suggestion instead (fallback: true) — never lost |
| Self-update (footer ✨ chip) | OTM_API_BASE + OTM_SERVICE_ID + OPERATOR_SHARED_SECRET |
footer shows no update chip; API answers 503 not_configured |
Session-signing note: the cookie key is derived from OPERATOR_SHARED_SECRET
(domain-separated HMAC) unless an explicit SESSION_SECRET is set; a managed
instance (OTM_MANAGED=true) with neither refuses to boot. OTM sign-ins are
recorded in OperatorMagicConsumed (jti + who + when); operator sessions last
1h and show a 🔑 support-session banner.
The repo
- Gitea:
https://git.poweredbyotm.com/bonna61/Bonna-Moon-Studio.git - Remote name here is
gitea(notorigin) — matches the FMB convention. - Local branch is
master; FMB and the platform both usemain. Rename before the first commit, or letgit checkout maincreate the tracking branch after the remote has one.
cd "/Users/bonnamoon/Projects/Bonna Moon Studio"
git fetch gitea && git checkout main # once the remote has content
Auth: macOS keychain. There is no ~/.gitconfig and no ~/.git-credentials on
this machine; credential.helper=osxkeychain is set at the git system level. A
git ls-remote gitea completes with no prompt, so pushes and fetches over
https://git.poweredbyotm.com just work. Nothing needs a token in the URL.
Commit identity is set locally in this repo (there's no global fallback — commits would fail without it):
user.name = Bonna
user.email = bonna@fullmoonbakehouse.com
The other Gitea address — ⚠️ TWO instances now, NAS is legacy
Corrected 2026-08-06 (the hard way): git.poweredbyotm.com and the home NAS
are separate Gitea instances. They diverged at some point; an earlier version
of this file claimed they were one instance with three addresses. They are not.
| Address | What it is |
|---|---|
https://git.poweredbyotm.com |
Production Gitea. Use this, always. Keychain auth. Holds this repo, tonym/platform (the real one — admin 0.111.0-era), bonna61/Moonbase, etc. |
http://192.168.0.5:3022 / http://167.237.9.184:3022 |
Legacy home-NAS Gitea (LAN / public IP of the same box). Being retired — Tony confirmed 2026-08-06. Its tonym/platform is frozen at ~0.80.0; pushing there deploys nothing. A stray bms commit (e2ea2b3) landed there before the divergence was discovered. |
Do not push platform work to the NAS. If a doc, remote URL, or memory points at
192.168.0.5:3022 / 167.237.9.184:3022, treat it as stale until verified.
The sibling repos (moonbase, moon-household-budget, otm-platform) embed an
access token directly in their remote URL against 192.168.0.5:3022. Those tokens
are only recorded in those URLs — the ~/Projects/CLAUDE.md that
moonbase/docs/git-deploy-guide.md refers to
does not exist on this machine. Read one back with:
git -C /Users/bonnamoon/Projects/moonbase remote get-url origin
Never commit a token.
Hosts
anvil=debian@51.81.80.250— OVH VPS, production, live. Serves*.poweredbyotm.comwith real Let's Encrypt certs. Wildcard*A-record, so any new<slug>.poweredbyotm.comresolves with zero DNS work. Host user isdebian, nottonym. Can't reach the home LAN — its builds use167.237.9.184:3022.forge=tonym@192.168.0.83— Unraid VM, staging,*.localhostnames with a local CA. Needs the home LAN.- OTM Admin: https://admin.poweredbyotm.com (anvil) — provisioning, template builds, per-service ops, logs.
A connected MCP server exposes the same control plane read/write (platform_status,
list_instances, list_templates, onboard, deploy + log tools). Call
platform_status or list_instances first for an overview. It points at live
production — treat onboard, deploys, and restarts as real-world actions.
Platform source: /Users/bonnamoon/Projects/platform (and otm-platform). Its
CLAUDE.md is the authority on deploys, the CI-gated auto-deploy webhook, and the
long list of anvil-vs-forge gotchas.
The plan (executed 2026-08-06)
One app with public pages plus an /admin back office — the site template
("Web — hosting + site + account", v0.11.0), same as home, overthemoon, and
vvts. Public site plus the drag-and-drop CMS page editor from @otm/account-panel.
Provisioned via onboard: customer Bonna Moon Studio, app/service slug
bonnamoonstudio, industry universal, owner bonna@bonnamoonstudio.com.
Live at https://bonnamoonstudio.poweredbyotm.com (admin at /admin). The
seeded owner password was captured and handed to Bonna at provisioning time; it is
deliberately not recorded anywhere in this repo.
What it is: Bonna's art studio — ceramics, carved wood, and sterling silver/ceramic jewelry. The prior site at bonnamoonstudio.com (shop temporarily closed during the studio rebuild) has the source content: product lines, collector testimonials, newsletter signup. That domain still points at the old site; cutover is a future DNS step.
Remaining notes:
- The
sitetemplate does not use this repo. Its code lives in the platform monorepo atapps/site; per-instance content lives in that instance's Postgres, not in git. This repo staying empty is expected. Giving it real code means authoring a new platform template and build chain — days, not minutes.