New how-to: How to self-host ObjectStack - #156
Conversation
New English how-to at content/blog/how-to-self-host-objectstack/: index.mdx plus cover-en.svg and one body diagram. Every command verified against ObjectStack 17.2.0 / Node 22 / PostgreSQL 16. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
New English-only how-to at content/blog/how-to-self-host-objectstack/: index.mdx, cover-en.svg, and one body diagram (what runs where). Every command was verified against what ships: @objectstack/cli 17.2.0 on Node 22 with PostgreSQL 16 — scaffold, os build, os start, and the five-step install verification. The container path is labelled as unrun (no Docker daemon available) rather than presented as verified. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
Re-verified every command, endpoint, env var and boundary claim in the draft against @objectstack/cli 17.2.0 (published on npm), the framework repo at origin/main, and the objectos docs repo — plus a live run: scaffold, os build, and os start against PostgreSQL 16 on Node 22. Reproduced exactly: the 2.2 KB artifact, "Runtime: 3 plugins", the "Plugins: 33 loaded" / "Driver: SqlDriver(pg)" / "Tenancy: single" banner, 68 tables created on an empty database, the /api/v1/ready body, and the missing-pg boot failure verbatim. Corrections: - OS_AUTH_SECRET did NOT behave as the draft claimed. `os start` mints and persists a secret to <home>/auth-secret (0600) via readOrCreateAuthSecret, so a server with no OS_AUTH_SECRET still authenticates — measured: an anonymous read returned 401 either way. The real failure is an ephemeral filesystem (next boot mints a different secret; sessions invalidated) and multi-replica divergence. Verified by booting with the variable unset, then removing the home directory and rebooting: different secret both times. The canonical Environment Variables doc states the same contract. - Verification check 4 no longer claims to catch a missing OS_AUTH_SECRET; it verifies the anonymous gate. Added the `code` key the REST seam actually returns, and the flat-vs-wrapped envelope note. - /api/v1/health sample body was missing the `timestamp` field. - Observability was understated: the runtime ships an OTLP/HTTP exporter (OS_OBS_EXPORTER=otlp), defaulting to noop, with no @opentelemetry dependency in the tree. What is absent is a Prometheus scrape endpoint. - Audit: sys_audit_log IS declared append-only and refuses API edits and deletes; what is missing is storage-level immutability. The draft said no immutability layer ships at all. - Bootstrap admin scoped to the single-tenancy posture; walled postures require OS_PLATFORM_OWNER_EMAIL and refuse startup without it. - Docs links pointed at objectstack.ai/docs paths that do not exist. Now docs.objectos.ai/docs/{configure/data-sources,deploy,reference/ environment-variables}, matching the site's own convention and the docs repo tree. - Bare-artifact boot: `npx os start` does not resolve outside a project, and a freshly installed CLI has no pg either. Stated both. - os build output block restored to the real shape; MySQL's two integrity gaps named concretely; container caveat now reports what was read from the published image build definition. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
Both SVGs declared `text{fill:#173a36}` in their <style> block while 15
<text> elements carried a `fill="…"` presentation attribute. A
presentation attribute has specificity 0, so any CSS rule beats it: the
type selector won on 11 of them and the `.small{fill:#657872}` class won
on the other 4. Every one rendered the default dark teal or grey instead
of the brown/green it was authored as.
Measured with getComputedStyle in Chromium before the change — all 15
reported SHADOWED, e.g. "IN YOUR REPO — BUILD" want #8a6a3a renders
#173a36, and "Receives only minimal, approved" want #a8703a renders
#657872 — and all 15 report honoured after it.
Fix is `fill="X"` -> `style="fill:X"` on <text> elements only, so an
inline style outranks both rules. The 22 <rect>/<path>/marker fills are
not shadowed and are untouched; no wording, geometry or colour value
changed. Re-rendered both at native size and inspected: the BUILD/RUN
captions, the mono artifact paths, the endpoint line and the
external-model note now carry their intended colours.
Not a legibility bug — every ground is light, so the earlier
dark-on-dark check passed correctly and the rendered text was always
readable. What was lost was the colour coding that carries the diagram's
meaning.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
… vocabulary Closed vocabulary from PR #160: every tag is either one of the seven tier-2 canonicals in this locale's rendering, or the proper noun of a product / company / protocol the post actually compares against. None qualifies. The post is an operations walkthrough; MCP, AI agents and the open business ontology appear as endpoint, closing framing and a glossary link rather than as its subject. PostgreSQL and Docker are the database and the container runtime this install uses, not products it compares against, and ObjectStack / ObjectOS are the site's own brand names, dropped by the PR #160 ruling. Empty is the measured outcome recorded on #158. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FeA1nwBz1ohH65dvffUGKr
|
Tags reconciled with the #78 closed vocabulary (#163): none — this post ends with It carries no tier-2 canonical by aboutness: MCP appears as an endpoint in the boot banner and the CTA, AI agents as closing framing, and the open business ontology as a one-sentence glossary link — none is the post's subject, which is the self-hosting procedure. Commit returns 0 lines. Why this is a comment and not the one-line note in the PR body, which is what #163 asks for: the body cannot be read back in full through the GitHub MCP read path. Two consecutive Generated by Claude Code Generated by Claude Code |
Fixes #92
New English-only how-to at
content/blog/how-to-self-host-objectstack/—index.mdx,cover-en.svg, and one body diagram (what-runs-where-en.svg).This branch resumes a draft that an earlier session left unpushed. The content commit is
0ed52a4; this round's work is the verification pass on top of it (6c477b7), which re-checked every command, endpoint, environment variable, version and boundary claim and corrected the ones that did not survive, plus an SVG patch round (58df4e5).What ships
A runnable end-to-end procedure: the compiled-artifact mental model, prerequisites, scaffold and compile, the database driver step, the four environment values that decide whether a deployment survives its second boot, starting the server, five verification checks that each fail differently, and an honest bill (upgrades, backups, on-call) plus a "when not to do this" section.
Primary keyword
self-host ai app platform; secondaryself-hosted internal app platform,run ai agents on own infrastructure. Slughow-to-self-host-objectstack. Answer in the first 100 words. Description 203 chars (≤ 230).Facts verified against shipping tooling
Sources:
objectstack-ai/objectstackatorigin/main(7da4cc2b), theobjectstack-ai/objectosdocs repo, the published npm package, and a live run — scaffold →os build→os startagainst PostgreSQL 16 on Node 22.22.2 with@objectstack/cli@17.2.0.Reproduced exactly, not merely read:
npm view @objectstack/cli version→17.2.0;packages/cli/package.jsonenginesinpackages/cliandpackages/create-objectstackAGENTS.md,Dockerfile,docker-compose.yml, skills bundlenpm create objectstack@latestrunos buildis an alias ofos compilepackages/cli/src/commands/build.tsextendsCompileData: 1 Objects 2 Fields,Runtime: 3 pluginspgfailure blockpg/mysql2/tediousare optional peerspackages/drivers/driver-sql/package.jsonpeerDependenciesMetaMode: production,Driver: SqlDriver(pg),Tenancy: single,Plugins: 33 loadedSELECT count(*) …→68/api/v1/readybody/healthliveness vs/readyreadiness splitpackages/runtime/src/route-ledger.ts401+UNAUTHENTICATEDcurl;packages/core/src/security/anonymous-deny.tsportAutoShiftAllowedis true only for dev orNODE_ENV=developmentMONGODB_MULTI_TENANT_UNSUPPORTED+ tenancy guarddocker/Dockerfile,.github/workflows/docker-publish.ymlCorrections made to the draft
OS_AUTH_SECRET— the draft's headline warning was wrong. It claimed a missing secret means "auth routes are silently skipped and the server runs unauthenticated".os startin fact mints and persists one toauth-secretin the runtime home directory (mode0600) viareadOrCreateAuthSecret. Measured: with the variable unset, an anonymous read still returned401. The real failure mode is an ephemeral filesystem — I removed the home directory, rebooted, and got a different secret, which invalidates every session — plus per-replica divergence. The canonicalreference/environment-variablesdoc states the same contract ("must be identical on every replica").OS_AUTH_SECRET(it cannot). It now verifies the anonymous gate, includes thecodekey the REST seam actually returns, and warns against a tolerant reader that accepts both the flat and wrapped envelopes./api/v1/healthsample body was missing thetimestampfield the handler returns.OS_OBS_EXPORTER=otlp), defaulting tonoop, with no@opentelemetrydependency anywhere in the tree. What is genuinely absent is a Prometheus scrape endpoint — push, not pull.sys_audit_logis declaredappend-onlyand refuses edits and deletes through the API; the draft said no immutability layer ships at all. What is missing is storage-level immutability against a directUPDATE.OS_PLATFORM_OWNER_EMAIL.objectstack.ai/docs/data-modeling/drivers,.../deployment/self-hosting; the docs tree hasdeploy/, notdeployment/). Nowdocs.objectos.ai/docs/{configure/data-sources, deploy, reference/environment-variables}, matching both the site's own convention (SecurityPage.astro) and the docs repo layout. These were the only twoobjectstack.ai/docslinks on the entire site.npx os startdoes not resolve outside a project, and a freshly installed CLI has nopgeither. Both now stated; the draft implied it simply worked.os buildoutput block restored to its real shape; MySQL's two integrity gaps named concretely (no upsert conflict target; unique index bounded by key length); container caveat now reports what was read from the published image build definition rather than only what was not run.Dedup and consistency
self-hosted-ai-app-platformandpower-platform-lock-in-dataverse-self-hostare linked as the argument, not restated.src/components/SecurityPage.astroorcontent/pages/*/trust-center.ts. They independently corroborate the post: "The open-source ObjectStack runtime does not need a license callback or product telemetry channel to operate", and the residency table's audit-logs / AI-context rows match the diagram's boundary.Gates
Union run after the content commit, at head
6c477b7, through the shared verify lock (VERDICT command-exit 0 · held the lock 83s · waited 0s):pnpm content:lintDate is in the future: Tue Sep 15 2026(expected for a future slot)pnpm checkResult (133 files): 0 errors, 0 warnings, 0 hintspnpm build867 page(s) builtpnpm seo:smokeSEO smoke test passed (866 HTML pages checked)git status --porcelainempty after the build — nothing generated for this English-only post.Browser verification
Served
dist/and drove Chromium at 1440×900 and 390×844, scrolling lazy images into view anddecode()-ing before judging:scrollWidth === clientWidthat both widths (no horizontal scroll)/en/glossary/ontology/and both dedup postsPatch round (
58df4e5) — 15 shadowed text fillsReview caught a real defect the round above missed. Both SVGs declare
text{fill:#173a36}in their<style>block while 15<text>elements carried afill="…"presentation attribute. A presentation attribute has specificity 0, so any CSS rule outranks it: the type selector won on 11 of them, and.small{fill:#657872}won on the other 4. Every one rendered the default dark teal or grey instead of the brown/green it was authored as — the same dead-attribute pattern #125/#133 swept out of 29 SVGs.Not a legibility bug: all grounds are light, so the earlier dark-on-dark check passed correctly. What was lost was the colour coding that carries the diagram's meaning — BUILD vs RUN captions, the mono artifact paths, the endpoint line, and the "outside your boundary" note.
Confirmed mechanically rather than by eye, with
getComputedStylein Chromium:grep -o '<text[^>]*fill="[^"]*"' cover-en.svg what-runs-where-en.svg | wc -lSHADOWEDhonouredSample of the before reading:
"IN YOUR REPO — BUILD"want#8a6a3arenders#173a36;"Receives only minimal, approved"want#a8703arenders#657872.Fix is
fill="X"→style="fill:X"on<text>elements only, so an inline style outranks both rules. The 22<rect>/<path>/ marker fills are not shadowed and are untouched — after the change the only remainingfill="attributes are 19<rect>and 3<path>. No wording, geometry or colour value changed; the diff is exactly 15 insertions and 15 deletions.Both SVGs re-rendered standalone at native size and inspected: captions brown/green, mono paths coloured, external-model note now matching its dashed arrow, nothing moved.
Gates for this round: SVG-only change, so
pnpm content:lintandpnpm checkwere re-run (VERDICT command-exit 0;Result (133 files): 0 errors, 0 warnings, 0 hints). The full union is not owed again — no.mdx, route, or page-generating input changed.Cadence
date: 2026-09-15T10:00:00+08:00, noupdated,status: published,topic: governance,audience: it.Out-of-scope findings
reference/environment-variables.mdxlistsOS_ARTIFACT_PATHas retired and boot-refusing, but the open-source runtime image presets it andos startreads it objectos#251 (unassigned, no labels):reference/environment-variables.mdxlistsOS_ARTIFACT_PATHas retired and boot-refusing, while the open-source runtime image presets it andos startreads it — plausibly scoped to the EE image, but the page does not say so. Not resolved here.🤖 Generated with Claude Code
Generated by Claude Code