From 03327962600882f51fed76b0117dd03c616d9eca Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 06:23:47 +0000 Subject: [PATCH 1/3] docs: add third-party developer onboarding and self-hosted deployment guides Evaluated the docs from an external (npm-consumer) developer's perspective: the getting-started section had no hands-on scaffold-to-API tutorial that doesn't assume an AI agent or the monorepo, and the deployment section had no self-hosting guide (only Vercel as a concrete target). - getting-started/your-first-project: scaffold via create-objectstack, generated layout and package surface, dev server, REST calls, manual model edits, validate gate, artifact build - deployment/self-hosting: systemd, Docker (multi-stage), Docker Compose with Postgres, required secrets (OS_AUTH_SECRET/OS_SECRET_KEY), health and readiness probes, reverse-proxy wiring, multi-node notes - create-objectstack README: replace stale template list (minimal-api/full-stack/plugin) with the real registry (blank + remote todo/compliance/content/contracts/procurement) and document --skip-skills and the identity-rewrite behavior - nav meta.json + cross-links from section indexes Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Em3ky9uu6zw2cYzVhqCij2 --- content/docs/deployment/index.mdx | 1 + content/docs/deployment/meta.json | 1 + content/docs/deployment/self-hosting.mdx | 254 +++++++++++++++++ content/docs/getting-started/index.mdx | 1 + content/docs/getting-started/meta.json | 1 + .../getting-started/your-first-project.mdx | 259 ++++++++++++++++++ packages/create-objectstack/README.md | 97 ++++--- 7 files changed, 563 insertions(+), 51 deletions(-) create mode 100644 content/docs/deployment/self-hosting.mdx create mode 100644 content/docs/getting-started/your-first-project.mdx diff --git a/content/docs/deployment/index.mdx b/content/docs/deployment/index.mdx index 4435bf1ef1..add495d69b 100644 --- a/content/docs/deployment/index.mdx +++ b/content/docs/deployment/index.mdx @@ -167,6 +167,7 @@ managed KMS / Vault provider behind the same `ICryptoProvider` seam. ## Related +- [Self-Hosted Deployment](/docs/deployment/self-hosting) - [Single-Environment Mode](/docs/deployment/single-project-mode) - [Environment-Scoped Routing](/docs/api/environment-routing) - [Publish, Versioning & Preview](/docs/deployment/publish-and-preview) diff --git a/content/docs/deployment/meta.json b/content/docs/deployment/meta.json index 31e07869a7..c1d71d1422 100644 --- a/content/docs/deployment/meta.json +++ b/content/docs/deployment/meta.json @@ -3,6 +3,7 @@ "icon": "Cloud", "pages": [ "index", + "self-hosting", "vercel", "production-readiness", "publish-and-preview", diff --git a/content/docs/deployment/self-hosting.mdx b/content/docs/deployment/self-hosting.mdx new file mode 100644 index 0000000000..3b09e3f92d --- /dev/null +++ b/content/docs/deployment/self-hosting.mdx @@ -0,0 +1,254 @@ +--- +title: Self-Hosted Deployment +description: Run a compiled ObjectStack app on your own infrastructure — bare Node.js, systemd, Docker, and Docker Compose with Postgres, including health checks, reverse-proxy wiring, and the secrets you must pin. +--- + +# Self-Hosted Deployment + +This guide takes the artifact produced by `os build` / `os compile` and runs it +on infrastructure **you** operate: a Linux host, a Docker container, or a +compose stack with Postgres. It complements the platform-specific +[Vercel guide](/docs/deployment/vercel) and assumes you have read +[Deployment Modes](/docs/deployment). + +The deployment model is deliberately simple: + +``` +objectstack.config.ts ──(os build, CI)──▶ dist/objectstack.json ──(os start, server)──▶ running app +``` + +- The **artifact** (`dist/objectstack.json`) is a portable, self-describing + JSON file — your entire app. Build it once in CI; the host needs no + TypeScript and no build step. +- **`os start`** boots a production server directly from that artifact + ([reference](/docs/getting-started/cli#os-start)). +- **Deployment config stays outside the artifact.** Database URL, secrets, and + environment identity are injected via `OS_*` environment variables or flags. + +## The minimum viable production environment + +Four values every self-hosted deployment must pin — everything else has a +workable default: + +| Variable | Why it must be set | +|:---|:---| +| `OS_DATABASE_URL` | Without it, data lands in a SQLite file under the ObjectStack home directory (`~/.objectstack`, or `/.objectstack` next to a project config) — fine for one box, wrong for containers. Use `postgres://…`, `libsql://…`, or a mounted `file:…` path. | +| `OS_AUTH_SECRET` | Session secret for the auth plugin (`AUTH_SECRET` is the legacy alias). Without it, `/api/v1/auth/*` is **silently skipped** — the server runs unauthenticated. | +| `OS_SECRET_KEY` | 32-byte master key encrypting every stored secret (`openssl rand -hex 32`). On a container's ephemeral filesystem the auto-minted key is **lost on restart**, making previously-encrypted secrets undecryptable. | +| `OS_PORT` | `os start` **fails loudly** if the port is busy (it never auto-shifts like `os dev`). Pin it and keep your reverse-proxy upstream in sync. | + +Generate strong values once and store them in your secret manager: + +```bash +OS_AUTH_SECRET=$(openssl rand -hex 32) +OS_SECRET_KEY=$(openssl rand -hex 32) +``` + +The full catalog is in +[Environment Variables](/docs/deployment/environment-variables). + +## Option 1 — Bare Node.js (systemd) + +The simplest deployment: Node 18+ and the CLI on a Linux host. + +```bash +# On the host — no repo clone, just the CLI and your artifact +npm install -g @objectstack/cli +scp dist/objectstack.json server:/opt/my-app/objectstack.json +``` + +```ini title="/etc/systemd/system/my-app.service" +[Unit] +Description=My ObjectStack App +After=network.target postgresql.service + +[Service] +Type=simple +User=objectstack +WorkingDirectory=/opt/my-app +Environment=NODE_ENV=production +Environment=OS_ARTIFACT_PATH=/opt/my-app/objectstack.json +Environment=OS_PORT=8080 +EnvironmentFile=/opt/my-app/secrets.env # OS_DATABASE_URL, OS_AUTH_SECRET, OS_SECRET_KEY +ExecStart=/usr/bin/os start +Restart=on-failure + +[Install] +WantedBy=multi-user.target +``` + +```bash +sudo systemctl enable --now my-app +curl -fsS http://localhost:8080/api/v1/health +``` + +Upgrades are atomic: replace the artifact file and restart the service. Roll +back by restoring the previous artifact. + +## Option 2 — Docker + +The artifact model maps cleanly onto containers: the image contains Node, the +CLI, and one JSON file. + +```dockerfile title="Dockerfile" +# ── Build stage: compile TypeScript metadata to the artifact ───────── +FROM node:22-slim AS build +WORKDIR /app +COPY package*.json ./ +RUN npm ci +COPY . . +RUN npx os build # → dist/objectstack.json + +# ── Runtime stage: CLI + artifact only ─────────────────────────────── +FROM node:22-slim +WORKDIR /srv/app +RUN npm install -g @objectstack/cli +COPY --from=build /app/dist/objectstack.json ./objectstack.json + +ENV NODE_ENV=production \ + OS_ARTIFACT_PATH=/srv/app/objectstack.json \ + OS_PORT=8080 +EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=3s --start-period=15s \ + CMD node -e "fetch('http://localhost:8080/api/v1/health').then(r=>{if(!r.ok)process.exit(1)}).catch(()=>process.exit(1))" + +CMD ["os", "start"] +``` + +```bash +docker build -t my-app . +docker run -p 8080:8080 \ + -e OS_DATABASE_URL="postgres://user:pass@db-host:5432/myapp" \ + -e OS_AUTH_SECRET \ + -e OS_SECRET_KEY \ + my-app +``` + + +**Never bake `OS_AUTH_SECRET` / `OS_SECRET_KEY` into the image.** Pass them at +runtime from your orchestrator's secret store. And never rely on the +auto-minted dev crypto key inside a container — it lives on the ephemeral +filesystem and dies with it. + + +## Option 3 — Docker Compose with Postgres + +A complete single-host production stack: + +```yaml title="docker-compose.yml" +services: + app: + build: . + ports: + - "8080:8080" + environment: + OS_DATABASE_URL: postgres://objectstack:${POSTGRES_PASSWORD}@db:5432/myapp + OS_AUTH_SECRET: ${OS_AUTH_SECRET} + OS_SECRET_KEY: ${OS_SECRET_KEY} + depends_on: + db: + condition: service_healthy + restart: unless-stopped + + db: + image: postgres:17 + environment: + POSTGRES_USER: objectstack + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: myapp + volumes: + - pgdata:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U objectstack -d myapp"] + interval: 5s + timeout: 3s + retries: 10 + restart: unless-stopped + +volumes: + pgdata: +``` + +```bash +# .env next to docker-compose.yml (never committed) +POSTGRES_PASSWORD=… +OS_AUTH_SECRET=… +OS_SECRET_KEY=… + +docker compose up -d +curl -fsS http://localhost:8080/api/v1/health +``` + +Prefer SQLite on a single small host? Skip the `db` service, mount a volume, +and point `OS_DATABASE_URL` at it: `file:/srv/data/app.db` (with +`- appdata:/srv/data` on the app service). See +[Drivers](/docs/data-modeling/drivers) for when to reach for which database. + +## Health checks & orchestration + +Every runtime exposes two probe endpoints — wire them into Docker +`HEALTHCHECK`, Kubernetes probes, or your load balancer: + +| Endpoint | Meaning | Use as | +|:---|:---|:---| +| `GET /api/v1/health` | Process is up and serving HTTP | Liveness probe | +| `GET /api/v1/ready` | Kernel booted, ready for traffic | Readiness probe | + +On Kubernetes, the same image works unchanged: mount the secrets as env vars, +point `readinessProbe` at `/api/v1/ready`, and scale — but read the multi-node +note below first. + +## Reverse proxy & TLS + +Terminate TLS in front of the app (Caddy, nginx, Traefik, or your cloud LB) +and keep three things in sync with the public origin: + +```bash +OS_AUTH_URL=https://app.example.com # auth callbacks / cookie origin +OS_TRUSTED_ORIGINS=https://app.example.com # CORS allow-list +OS_PORT=8080 # must match the proxy upstream +``` + +```caddyfile title="Caddyfile" +app.example.com { + reverse_proxy localhost:8080 +} +``` + +A drifted port or origin is the classic self-hosting failure: the app runs, +but logins bounce and browsers block API calls. Enable HSTS and tune security +headers only after TLS is confirmed — see +[Production Readiness](/docs/deployment/production-readiness). + +## Scaling beyond one node + +The default in-process coordination (locks, queues, schedules) is +single-node. Before running replicas, set `OS_CLUSTER_DRIVER` — the runtime +then treats the deployment as multi-node and **refuses to boot without an +explicit `OS_SECRET_KEY`** rather than minting per-node keys that can't +decrypt each other's secrets. All replicas must share the same +`OS_SECRET_KEY`, `OS_AUTH_SECRET`, and database. See +[Cluster](/docs/kernel/cluster). + +## Go-live + +Before pointing real users at the deployment, walk the +[Production Readiness](/docs/deployment/production-readiness) checklist — +security headers, rate limits, metrics, error reporting, backup/restore +drills, and data-retention windows. + + +**Your self-hosted app is AI-operable out of the box:** every deployment +serves an MCP server at `/api/v1/mcp` under the same permissions and RLS. +Disable with `OS_MCP_SERVER_ENABLED=false`. See +[Your app as an MCP server](/docs/api#your-app-as-an-mcp-server). + + +## Related + +- [Deployment Modes](/docs/deployment) — the map of local / standalone / Cloud +- [`os start` reference](/docs/getting-started/cli#os-start) — every flag and env var +- [Environment Variables](/docs/deployment/environment-variables) — the full catalog +- [Deploy to Vercel](/docs/deployment/vercel) — the serverless alternative +- [Troubleshooting & FAQ](/docs/deployment/troubleshooting) diff --git a/content/docs/getting-started/index.mdx b/content/docs/getting-started/index.mdx index da6e34d8f0..9d650cc352 100644 --- a/content/docs/getting-started/index.mdx +++ b/content/docs/getting-started/index.mdx @@ -197,4 +197,5 @@ For more troubleshooting, see the [Troubleshooting & FAQ](/docs/deployment/troub - [Architecture](/docs/concepts/architecture) — The three-layer protocol stack - [Glossary](/docs/getting-started/glossary) — Key terminology - [Build with Claude Code](/docs/getting-started/build-with-claude-code) — Build your first app end-to-end with an agent +- [Your First Project](/docs/getting-started/your-first-project) — The hands-on path: scaffold from npm, extend by hand, call the API - [Anatomy of an ObjectStack App](/docs/getting-started/quick-start) — Read the metadata an agent writes, so you can verify it diff --git a/content/docs/getting-started/meta.json b/content/docs/getting-started/meta.json index 4958782f6b..4306d0f4f6 100644 --- a/content/docs/getting-started/meta.json +++ b/content/docs/getting-started/meta.json @@ -6,6 +6,7 @@ "index", "how-ai-development-works", "build-with-claude-code", + "your-first-project", "quick-start", "examples", "cli", diff --git a/content/docs/getting-started/your-first-project.mdx b/content/docs/getting-started/your-first-project.mdx new file mode 100644 index 0000000000..cfd2d98b3a --- /dev/null +++ b/content/docs/getting-started/your-first-project.mdx @@ -0,0 +1,259 @@ +--- +title: Your First Project +description: Scaffold a standalone ObjectStack project with npm, understand what was generated, extend the data model, call the REST API, and build a deployable artifact — no monorepo checkout, no AI agent required. +--- + +# Your First Project + +This is the hands-on path for developers building **on** ObjectStack from the +published npm packages — you never clone the framework repository. In about ten +minutes you will scaffold a project, run it, add a field by hand, call the +generated REST API, and compile the artifact you would deploy. + + +**Two ways to build.** ObjectStack is designed AI-first: normally +[an agent authors the metadata and you verify it](/docs/getting-started/build-with-claude-code). +This page walks the same ground *manually* so you understand every file the +agent would touch — useful for evaluating the platform, wiring CI, or working +without an agent. The two paths produce identical projects. + + +## Prerequisites + +| Tool | Minimum | Check | +|:---|:---|:---| +| **Node.js** | 18+ | `node --version` | +| A package manager | npm 9+ / pnpm 8+ / yarn / bun | `npm --version` | + +That's all. A standalone project does **not** need pnpm, TypeScript pre-installed, +or a database server — the default template runs on an in-memory driver, and +TypeScript comes in as a dev dependency. + +## 1. Scaffold + +```bash +npm create objectstack@latest my-app +cd my-app +``` + +The scaffolder (`create-objectstack`) does four things: + +1. **Copies a template** into `my-app/` and rewrites its identity — the npm + package name, the manifest `name`/`displayName`, and a **namespace** derived + from your project name (`my-app` → `my_app`). Every object in the template is + renamed to carry that prefix (`my_app_note`), which is what + `os validate` later enforces. +2. **Installs dependencies** (pnpm if available, otherwise npm). +3. **Installs the AI skills bundle** (`npx skills add objectstack-ai/framework --all`) + so a coding agent is productive in the project from the first prompt. +4. **Writes `AGENTS.md`** (and `.github/copilot-instructions.md`) with the + project conventions — including the rule to run `npm run validate` after + every metadata change. + +Steps 3–4 cost nothing if you never use an agent; skip them with +`--skip-skills` if you prefer. + +### Templates + +| Template | Source | Description | +|:---|:---|:---| +| `blank` *(default)* | bundled, works offline | One object, REST API — a clean slate | +| `todo` | remote | Universal task & project management starter | +| `compliance` | remote | Compliance posture & evidence management (SOC2 / ISO27001) | +| `content` | remote | Content marketing pipeline — editorial calendar & channel ROI | +| `contracts` | remote | Post-signature CLM — approvals, obligations, renewals | +| `procurement` | remote | Source-to-pay — vendors, POs, receipts, invoice matching | + +```bash +npm create objectstack@latest my-app -- --template todo +``` + +Remote templates are fetched from the +[`objectstack-ai/templates`](https://github.com/objectstack-ai/templates) +repository at scaffold time and need network access; `blank` is bundled inside +the npm package and always works offline. + + +`os init` (from `@objectstack/cli`) is an alternative scaffolder with `app` / +`plugin` / `empty` templates — see the [CLI reference](/docs/getting-started/cli#os-init). +`npm create objectstack` is the recommended entry point for new standalone projects. + + +## 2. What was generated + +``` +my-app/ +├── objectstack.config.ts # defineStack() — the single entry point +├── objectstack.manifest.json # name, namespace, version +├── package.json +├── tsconfig.json +├── AGENTS.md # conventions for coding agents +└── src/ + └── objects/ + ├── index.ts # barrel — re-exports every object + └── note.object.ts # a sample object +``` + +Two files matter most: + +**`objectstack.config.ts`** wires everything together. There is no +filename-suffix magic — metadata exists in the app only if it is imported here: + +```typescript title="objectstack.config.ts" +import { defineStack } from '@objectstack/spec'; +import * as objects from './src/objects/index.js'; + +export default defineStack({ + manifest: { + id: 'my-app', + namespace: 'my_app', + version: '0.1.0', + type: 'app', + name: 'My App', + }, + objects: Object.values(objects), + api: { + rest: { enabled: true, basePath: '/api' }, + }, +}); +``` + +**`src/objects/note.object.ts`** is a complete data model — schema, validation, +API surface, and searchability in one typed definition: + +```typescript title="src/objects/note.object.ts" +import { ObjectSchema, Field } from '@objectstack/spec/data'; + +export const Note = ObjectSchema.create({ + name: 'my_app_note', // namespace-prefixed, snake_case + label: 'Note', + pluralLabel: 'Notes', + icon: 'sticky-note', + + fields: { + title: Field.text({ label: 'Title', required: true, searchable: true, maxLength: 200 }), + body: Field.longText({ label: 'Body' }), + }, + + enable: { apiEnabled: true, searchable: true }, +}); +``` + +### The packages you depend on + +A standalone project uses a handful of published packages — this is the entire +framework surface you install: + +| Package | Role | +|:---|:---| +| `@objectstack/spec` | The protocol: `defineStack`, `ObjectSchema`, `Field.*` builders, all Zod schemas. Your metadata imports only this. | +| `@objectstack/runtime` | The kernel that interprets metadata at runtime. | +| `@objectstack/driver-memory` | In-memory database driver for development. Swap for SQLite / Postgres / MongoDB in production — [no code changes](/docs/data-modeling/drivers). | +| `@objectstack/plugin-hono-server` | The HTTP server that mounts the generated REST API. | +| `@objectstack/cli` *(dev)* | The `os` / `objectstack` CLI: `dev`, `validate`, `build`, `start`. | + +## 3. Run it + +```bash +npm run dev # objectstack dev — hot reload +``` + +Or with the visual Console (data browser, metadata explorer, API docs): + +```bash +npx os dev --ui # then open http://localhost:3000/_console/ +``` + +## 4. Call your API + +The REST API is derived from the object metadata — you wrote no routes. With +the dev server running: + +```bash +# Create a record +curl -X POST http://localhost:3000/api/v1/data/my_app_note \ + -H "Content-Type: application/json" \ + -d '{"title": "Hello ObjectStack", "body": "Created via the generated API"}' + +# Query records (filtering, sorting, pagination built in) +curl "http://localhost:3000/api/v1/data/my_app_note?select=title&sort=-created_at&top=10" +``` + +See the [Data API](/docs/api/data-api) for the full CRUD, batch, and analytics +surface, and the [Client SDK](/docs/api/client-sdk) for the typed TypeScript +client (`@objectstack/client`). + +## 5. Extend the model + +Add a field and a second object by hand — the same edit an agent would make. + +```typescript title="src/objects/note.object.ts" + fields: { + title: Field.text({ label: 'Title', required: true, searchable: true, maxLength: 200 }), + body: Field.longText({ label: 'Body' }), + status: Field.select({ + label: 'Status', + options: [ + { label: 'Draft', value: 'draft', default: true }, + { label: 'Published', value: 'published' }, + ], + }), + }, +``` + +New objects follow the same pattern: create `src/objects/.object.ts` with +a `name` of `_`, then re-export it from +`src/objects/index.ts` (the barrel is what the config imports). + +After **every** metadata change, run the gate: + +```bash +npm run validate +``` + +`os validate` checks schema compliance, CEL predicate scoping, and widget +bindings — the classes of mistakes that otherwise fail *silently* at runtime. +See [Validating Metadata](/docs/getting-started/validating-metadata). + +## 6. Build the deployable artifact + +```bash +npm run build # objectstack build → dist/objectstack.json +``` + +The artifact is a **portable, self-describing deployment unit**: one JSON file +containing your entire app. Any server with `@objectstack/cli` can boot it — +no TypeScript, no build step on the host: + +```bash +npx os start --artifact ./dist/objectstack.json --port 8080 +``` + +Database URL, auth secret, and environment identity stay *outside* the +artifact and are injected at boot via flags or `OS_*` environment variables — +which is exactly what makes it container-friendly. + +## Next steps + + + + + + + diff --git a/packages/create-objectstack/README.md b/packages/create-objectstack/README.md index 55db066257..11898bbda2 100644 --- a/packages/create-objectstack/README.md +++ b/packages/create-objectstack/README.md @@ -1,15 +1,19 @@ # create-objectstack -Scaffold a new [ObjectStack](https://objectstack.com) project in seconds. +Scaffold a new [ObjectStack](https://objectstack.ai) project in seconds. + +```bash +npm create objectstack@latest my-app +``` ## Usage ```bash -# Interactive — creates project in a new directory +# Create a project in a new directory (blank template) npx create-objectstack my-app # Use a specific template -npx create-objectstack my-app --template full-stack +npx create-objectstack my-app --template todo # Scaffold in the current directory npx create-objectstack @@ -20,80 +24,71 @@ npx create-objectstack my-app --skip-install ## Templates -| Template | Description | -| --- | --- | -| `minimal-api` *(default)* | Server + memory driver + 1 object + REST API | -| `full-stack` | Server + UI + auth + 3 CRM objects (Contact, Company, Deal) | -| `plugin` | Reusable plugin skeleton with test setup | +| Template | Source | Description | +| --- | --- | --- | +| `blank` *(default)* | bundled (offline) | Minimal starter — one object, REST API, ready to extend | +| `todo` | remote | Universal task & project management starter | +| `compliance` | remote | Compliance posture & evidence management (SOC2 / ISO27001) | +| `content` | remote | Content marketing pipeline — editorial calendar & channel ROI | +| `contracts` | remote | Post-signature CLM — approvals, obligations, renewals | +| `procurement` | remote | Source-to-pay — vendors, POs, receipts, invoice matching | + +Remote templates are fetched from +[`objectstack-ai/templates`](https://github.com/objectstack-ai/templates) at +scaffold time and require network access; `blank` is bundled and always works +offline. ## Options | Option | Description | | --- | --- | | `[name]` | Project name (defaults to current directory name) | -| `-t, --template ` | Project template (default: `minimal-api`) | +| `-t, --template ` | Project template (default: `blank`) | | `--skip-install` | Skip dependency installation | +| `--skip-skills` | Skip installing the ObjectStack AI skills bundle | | `-V, --version` | Show version | | `-h, --help` | Show help | -## What Gets Generated +## What it does + +1. Copies the template into the target directory and rewrites its identity: + `package.json` name, `objectstack.manifest.json` name/displayName, the + `objectstack.config.ts` manifest literals, and the README title. A + **namespace** is derived from the project name (`my-app` → `my_app`) and + every object name in the template is re-prefixed to match + (`blank_note` → `my_app_note`). +2. Installs dependencies (pnpm if available, otherwise npm). +3. Installs the ObjectStack AI skills bundle for coding agents + (`npx skills add objectstack-ai/framework --all`). +4. Writes `AGENTS.md` and `.github/copilot-instructions.md` with the project + conventions — unless the template ships its own. -### minimal-api +## What Gets Generated (blank) ``` my-app/ -├── objectstack.config.ts +├── objectstack.config.ts # defineStack() entry point +├── objectstack.manifest.json # name, namespace, version ├── package.json ├── tsconfig.json ├── .gitignore ├── README.md +├── AGENTS.md # conventions for coding agents └── src/ └── objects/ ├── index.ts - └── task.ts + └── note.object.ts ``` -### full-stack +Next steps inside the project: +```bash +npm run dev # start the dev server +npm run validate # verify metadata: schema + predicates + bindings ``` -my-app/ -├── objectstack.config.ts -├── package.json -├── tsconfig.json -├── .gitignore -├── README.md -└── src/ - ├── objects/ - │ ├── index.ts - │ ├── contact.ts - │ ├── company.ts - │ └── deal.ts - ├── views/ - │ ├── contact_list.ts - │ ├── company_list.ts - │ └── deal_list.ts - └── apps/ - ├── index.ts - └── crm.ts -``` - -### plugin -``` -my-plugin/ -├── objectstack.config.ts -├── package.json -├── tsconfig.json -├── .gitignore -├── README.md -├── src/ -│ ├── index.ts -│ └── objects/ -│ ├── index.ts -│ └── sample.ts -└── test/ - └── sample.test.ts -``` +See the docs: +[Your First Project](https://docs.objectstack.ai/docs/getting-started/your-first-project). ## License From 2a0eb7054696cb880e7b95786b8468afb43551b5 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 06:26:15 +0000 Subject: [PATCH 2/3] docs: avoid reserved word 'role' in package table header (ADR-0090 D3) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Em3ky9uu6zw2cYzVhqCij2 --- content/docs/getting-started/your-first-project.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/getting-started/your-first-project.mdx b/content/docs/getting-started/your-first-project.mdx index cfd2d98b3a..10d8afe553 100644 --- a/content/docs/getting-started/your-first-project.mdx +++ b/content/docs/getting-started/your-first-project.mdx @@ -144,7 +144,7 @@ export const Note = ObjectSchema.create({ A standalone project uses a handful of published packages — this is the entire framework surface you install: -| Package | Role | +| Package | What it does | |:---|:---| | `@objectstack/spec` | The protocol: `defineStack`, `ObjectSchema`, `Field.*` builders, all Zod schemas. Your metadata imports only this. | | `@objectstack/runtime` | The kernel that interprets metadata at runtime. | From 254074ec7ca51d61109495e7898e583106890955 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 06:29:08 +0000 Subject: [PATCH 3/3] docs: use plain text fence for Caddyfile sample (shiki has no caddyfile grammar) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Em3ky9uu6zw2cYzVhqCij2 --- content/docs/deployment/self-hosting.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/deployment/self-hosting.mdx b/content/docs/deployment/self-hosting.mdx index 3b09e3f92d..e34f41f82f 100644 --- a/content/docs/deployment/self-hosting.mdx +++ b/content/docs/deployment/self-hosting.mdx @@ -210,7 +210,7 @@ OS_TRUSTED_ORIGINS=https://app.example.com # CORS allow-list OS_PORT=8080 # must match the proxy upstream ``` -```caddyfile title="Caddyfile" +```text title="Caddyfile" app.example.com { reverse_proxy localhost:8080 }