From 8ede9631ef5b99d681bb51c8123016e52dd68b90 Mon Sep 17 00:00:00 2001 From: fullstackjam Date: Sun, 17 May 2026 12:00:04 +0800 Subject: [PATCH] docs: align user docs and repo docs with implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed all user-facing docs (src/docs/) and repo-level docs against the actual code and fixed inaccuracies surfaced by the audit: - README.md: fix CI badge URL (deploy.yml → ci.yml); replace tag-based "Deployment" section with the actual push-to-main flow; list all 5 D1 tables instead of 2. - RELEASE.md: delete (described a tag-based release process that does not exist; push-to-main auto-deploys per ci.yml). - CLAUDE.md: add missing scripts (lint, validate, install:hooks); fix table count (4 → 5, add config_revisions); correct packages enrichment description; cross-link AGENTS.md + docs/HARNESS.md. - src/docs/api-reference.md: rewrite to match the actual handlers — every endpoint shape (request/response/status), correct rate limits (CONFIG_READ/WRITE/SEARCH all 30/min), remove fabricated X-RateLimit-* headers, split dashboard /api/configs/:slug from CLI /:username/:slug/config, fix install endpoint Content-Type. - src/docs/config-options.md: document validation limits — custom_script 10K, dotfiles_repo HTTPS + 500, alias 2-20 chars + reserved words, packages 500/200; fix base_preset default; document snapshot.macos_prefs[].host. - src/docs/personal.md: remove false "Dotfiles cloned and linked" line from the first install transcript (no dotfiles_repo configured yet). - src/docs/teams.md: correct visibility guidance — private is owner-only (403 for other users), teams should use unlisted. - src/docs/snapshot.md: "Before installing" → "Before applying" in the restore section. - src/docs/what-is-openboot.md: spaced em-dash for style consistency. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 7 +- README.md | 20 +-- RELEASE.md | 134 ---------------- src/docs/api-reference.md | 301 ++++++++++++++++++++++------------- src/docs/config-options.md | 14 +- src/docs/personal.md | 1 - src/docs/snapshot.md | 2 +- src/docs/teams.md | 2 +- src/docs/what-is-openboot.md | 2 +- 9 files changed, 218 insertions(+), 265 deletions(-) delete mode 100644 RELEASE.md diff --git a/CLAUDE.md b/CLAUDE.md index 31c3832..69bf750 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,16 +14,21 @@ The CLI lives at [openbootdotdev/openboot](https://github.com/openbootdotdev/ope npm run dev # Local dev server npm run build # Production build npm run check # Type checking (svelte-kit sync + svelte-check) +npm run lint # ESLint +npm run validate # check + lint + test (the harness gate) npm test # Run all tests (vitest) npx vitest run src/lib/server/auth.test.ts # Run a single test file npx vitest run -t "test name" # Run test by name npm run test:coverage # Tests with coverage report +npm run install:hooks # Install git pre-commit/pre-push hooks # Database wrangler d1 migrations apply openboot --local # Apply migrations locally wrangler d1 migrations apply openboot --remote # Apply migrations to production ``` +See [AGENTS.md](./AGENTS.md) for harness invariants (no `process.env`, no `console.log` in server code, D1 access scoping) and [docs/HARNESS.md](./docs/HARNESS.md) for the full enforcement model. + Local dev requires `.dev.vars` with `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`, `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`. ## Architecture @@ -57,7 +62,7 @@ Three auth flows: ### Database -D1 (SQLite), no ORM — direct parameterized SQL via `env.DB.prepare(sql).bind(...)`. Four tables: `users`, `configs`, `api_tokens`, `cli_auth_codes`. The `configs.packages` field is a JSON array (stored as `{name, type}` objects; the config endpoint transforms to `{name, desc}` on read, filling descriptions from `package-metadata.ts`). Config visibility: `public` (discoverable), `unlisted` (accessible but not listed), `private` (owner-only, 403 on install). +D1 (SQLite), no ORM — direct parameterized SQL via `env.DB.prepare(sql).bind(...)`. Five tables: `users`, `configs`, `config_revisions`, `api_tokens`, `cli_auth_codes`. The `configs.packages` field is a JSON array of `{name, type}` objects; on read, endpoints enrich each entry with a `desc` filled from `package-metadata.ts`. Config visibility: `public` (discoverable), `unlisted` (accessible but not listed), `private` (owner-only, 403 on install). **D1 limitation**: No `ALTER TABLE DROP COLUMN`. Plan column removals via new table + data migration. diff --git a/README.md b/README.md index 70e91e5..215b5ff 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Web dashboard and install API for [OpenBoot](https://github.com/openbootdotdev/openboot). -[![Deploy](https://github.com/openbootdotdev/openboot.dev/actions/workflows/deploy.yml/badge.svg)](https://github.com/openbootdotdev/openboot.dev/actions/workflows/deploy.yml) +[![CI](https://github.com/openbootdotdev/openboot.dev/actions/workflows/ci.yml/badge.svg)](https://github.com/openbootdotdev/openboot.dev/actions/workflows/ci.yml) **Live at [openboot.dev](https://openboot.dev)** @@ -41,21 +41,7 @@ GOOGLE_CLIENT_SECRET=... ## Deployment -**Tag-based releases** — production deploys only happen when you create a version tag: - -```bash -git tag v1.0.0 -git push origin v1.0.0 -``` - -This triggers: -1. Tests + build -2. Database migrations -3. Deployment to openboot.dev - -Push to `main` only runs CI (tests + build), no deployment. - -See [RELEASE.md](./RELEASE.md) for full release process. +Push to `main` runs CI (type check + tests + build) and, on success, auto-deploys to [openboot.dev](https://openboot.dev). PRs run CI only. The deploy job lives in `.github/workflows/ci.yml`; see [docs/HARNESS.md](./docs/HARNESS.md) for the full pipeline. Secrets needed: `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID` @@ -82,7 +68,7 @@ Secrets needed: `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID` ## Database -D1 (SQLite). Two tables: `users` and `configs`. See `migrations/` for schema. Key fields: +D1 (SQLite). Tables: `users`, `configs`, `config_revisions`, `api_tokens`, `cli_auth_codes`. See `migrations/` for schema. Key fields on `configs`: - `configs.packages` — JSON array of {name, type, desc} - `configs.snapshot` — JSON object from CLI `openboot snapshot` diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 2ded5f1..0000000 --- a/RELEASE.md +++ /dev/null @@ -1,134 +0,0 @@ -# Release Process - -This project uses **tag-based releases**. Production deployments only happen when you create a Git tag. - -## Quick Release - -```bash -# 1. Ensure main branch is ready -git checkout main -git pull - -# 2. Create and push a version tag -git tag v1.0.0 -git push origin v1.0.0 -``` - -This triggers the production deployment workflow automatically. - -## Release Workflow - -### 1. Development & Testing - -- Push commits to `main` branch -- CI runs automatically (tests + build) -- **No deployment happens** - -### 2. Ready to Release - -When you're ready to deploy to production: - -```bash -# Create a tag following semantic versioning -git tag v1.2.3 - -# Push the tag to GitHub -git push origin v1.2.3 -``` - -### 3. Automated Deployment - -The tag push triggers `.github/workflows/deploy.yml`: - -1. ✅ Run tests -2. ✅ Build application -3. ✅ Run database migrations -4. ✅ Deploy to Cloudflare Workers (openboot.dev) - -### 4. Verify Deployment - -Check GitHub Actions: https://github.com/openbootdotdev/openboot.dev/actions - -## Version Numbering - -Follow [Semantic Versioning](https://semver.org/): - -- **v1.0.0** → Major release (breaking changes) -- **v1.1.0** → Minor release (new features, backward compatible) -- **v1.1.1** → Patch release (bug fixes) - -### Examples - -```bash -# Bug fix -git tag v1.0.1 -git push origin v1.0.1 - -# New feature -git tag v1.1.0 -git push origin v1.1.0 - -# Breaking change -git tag v2.0.0 -git push origin v2.0.0 -``` - -## Rollback - -If a deployment has issues: - -```bash -# Option 1: Deploy previous version -git push origin v1.0.0 --force-with-lease - -# Option 2: Create hotfix tag -git tag v1.0.2 -git push origin v1.0.2 -``` - -## Manual Deployment - -You can also trigger deployment manually from GitHub: - -1. Go to Actions → Deploy to Production -2. Click "Run workflow" -3. Select `main` branch -4. Click "Run workflow" - -## Release Checklist - -Before tagging a release: - -- [ ] All tests passing on `main` -- [ ] Migrations tested locally -- [ ] Breaking changes documented -- [ ] Version number follows semver -- [ ] Previous version tagged (for rollback reference) - -## Migration Strategy - -Database migrations run automatically before deployment. Ensure: - -1. Migration is backward compatible (if possible) -2. Migration tested with `wrangler d1 migrations apply openboot --local` -3. Large migrations coordinated with zero-downtime deployment - -## FAQ - -**Q: Can I delete a tag?** -```bash -# Delete local tag -git tag -d v1.0.0 - -# Delete remote tag -git push origin :refs/tags/v1.0.0 -``` - -**Q: What happens if deployment fails?** -- GitHub Actions will show the error -- Previous version remains deployed -- Fix the issue and create a new tag - -**Q: How do I see what's deployed?** -- Check latest tag: `git describe --tags --abbrev=0` -- View deployment history in GitHub Actions diff --git a/src/docs/api-reference.md b/src/docs/api-reference.md index 3046bb8..99ffa83 100644 --- a/src/docs/api-reference.md +++ b/src/docs/api-reference.md @@ -17,13 +17,16 @@ https://openboot.dev ## Authentication -Some endpoints require authentication. Include your auth token in the `Authorization` header: +Most write endpoints and dashboard-style read endpoints require authentication. Two mechanisms are supported: -``` -Authorization: Bearer YOUR_TOKEN -``` +- **Browser session** — set automatically after OAuth login (httpOnly `session` cookie). +- **CLI token** — sent via the `Authorization` header: + + ``` + Authorization: Bearer obt_xxxxxxxxxxxxxxxx + ``` -Get your token by running `openboot login` — it's saved to `~/.openboot/auth.json`. + Get a token by running `openboot login` — it's saved to `~/.openboot/auth.json` and starts with the `obt_` prefix. --- @@ -42,45 +45,67 @@ GET /api/configs **Response:** ```json -[ - { - "id": "cfg_abc123", - "slug": "my-setup", - "name": "My Dev Setup", - "description": "Personal development environment", - "visibility": "unlisted", - "install_count": 12, - "created_at": "2024-01-15T10:30:00Z", - "updated_at": "2024-02-10T14:20:00Z" - } -] +{ + "username": "alex", + "configs": [ + { + "id": "cfg_abc123", + "user_id": "usr_xyz", + "slug": "my-setup", + "name": "My Dev Setup", + "description": "Personal development environment", + "base_preset": "developer", + "packages": [{ "name": "node", "type": "formula" }], + "custom_script": "", + "dotfiles_repo": "", + "snapshot": null, + "snapshot_at": null, + "visibility": "unlisted", + "alias": null, + "install_count": 12, + "forked_from": null, + "created_at": "2024-01-15T10:30:00Z", + "updated_at": "2024-02-10T14:20:00Z" + } + ] +} ``` -### Get Config +`packages` and `snapshot` are returned as parsed JSON (not strings). + +### Get Config (Dashboard) -Get a specific config by username and slug. Public and unlisted configs are accessible without auth. Private configs require authentication and ownership. +Get one of the authenticated user's configs by slug, with installation URL. This is the dashboard endpoint — for the CLI-flattened shape, see [`GET /:username/:slug/config`](#get-config-cli) further down. ``` GET /api/configs/:slug ``` -**Auth required:** Only for private configs +**Auth required:** Yes (returns 401 otherwise). Only returns configs owned by the caller; other users' configs return 404 regardless of visibility. **Response:** ```json { - "id": "cfg_abc123", - "slug": "my-setup", - "name": "My Dev Setup", - "description": "Personal development environment", - "base_preset": "developer", - "packages": [ - { "name": "node", "type": "formula", "desc": "JavaScript runtime built on V8 engine" } - ], - "custom_script": "mkdir -p ~/projects", - "dotfiles_repo": "https://github.com/user/dotfiles.git", - "visibility": "unlisted" + "config": { + "id": "cfg_abc123", + "slug": "my-setup", + "name": "My Dev Setup", + "description": "Personal development environment", + "base_preset": "developer", + "packages": [ + { "name": "node", "type": "formula" } + ], + "custom_script": "mkdir -p ~/projects", + "dotfiles_repo": "https://github.com/user/dotfiles.git", + "visibility": "unlisted", + "alias": "mine", + "snapshot": { "...": "parsed snapshot or null" }, + "macos_prefs": [ + { "domain": "com.apple.dock", "key": "tilesize", "value": "48", "type": "int" } + ] + }, + "install_url": "https://openboot.dev/mine" } ``` @@ -104,22 +129,29 @@ POST /api/configs "packages": [ { "name": "node", "type": "formula" } ], - "visibility": "unlisted" + "custom_script": "", + "dotfiles_repo": "", + "visibility": "unlisted", + "alias": "mine" } ``` -**Response:** +**Response:** `201 Created` ```json { "id": "cfg_abc123", - "slug": "my-setup" + "slug": "my-setup", + "alias": "mine", + "install_url": "https://openboot.dev/mine" } ``` +Constraints: name ≤ 100 chars; description ≤ 500 chars; alias 2–20 chars (lowercase alphanumeric + hyphens, reserved words rejected); max 500 packages; max 20 configs per user. + ### Update Config -Update an existing config. +Update an existing config. All fields are optional — only the fields you send are changed. ``` PUT /api/configs/:slug @@ -127,9 +159,20 @@ PUT /api/configs/:slug **Auth required:** Yes (must own the config) -**Request body:** Same as create (all fields optional) +**Request body:** Same shape as create. Setting `alias` to `""` or `null` removes the alias. + +**Response:** + +```json +{ + "success": true, + "slug": "my-setup", + "alias": "mine", + "install_url": "https://openboot.dev/mine" +} +``` -**Response:** Updated config object +Renaming via `name` regenerates the slug — the new slug is returned. ### Delete Config @@ -141,72 +184,114 @@ DELETE /api/configs/:slug **Auth required:** Yes (must own the config) -**Response:** `204 No Content` +**Response:** `200 OK` + +```json +{ "success": true } +``` -### Create Config from Snapshot +### Create or Update Config from Snapshot -Upload a snapshot captured by `openboot snapshot` to create a config. +Upload a snapshot captured by `openboot snapshot`. Creates a new config, or updates an existing one when `config_slug` is provided. `POST` and `PUT` both route here. ``` POST /api/configs/from-snapshot +PUT /api/configs/from-snapshot ``` **Auth required:** Yes +**Payload size limit:** 1 MB + **Request body:** ```json { "name": "My Mac Setup", + "description": "Captured 2026-05-17", + "visibility": "unlisted", + "config_slug": "my-mac-setup", + "message": "added pnpm", "snapshot": { + "matched_preset": "developer", "packages": { "formulae": ["node", "go"], "casks": ["visual-studio-code"], "npm": ["typescript"], "taps": ["homebrew/cask-fonts"] }, - "macos_prefs": [...], - "shell": {...}, - "git": {...} + "macos_prefs": [ + { "domain": "com.apple.dock", "key": "tilesize", "value": "48", "type": "int" } + ], + "shell": { "...": "..." }, + "git": { "...": "..." } } } ``` -**Response:** +- `config_slug` is optional; when present, the snapshot updates that existing config and records a revision with `message`. +- `snapshot.packages` must be an object with `formulae`, `casks`, `npm`, `taps` arrays. + +**Response:** the created or updated config row, with `packages` and `snapshot` parsed. Status is `201 Created` for a new config or `200 OK` for an update. ```json { - "slug": "my-mac-setup" + "id": "cfg_abc123", + "slug": "my-mac-setup", + "name": "My Mac Setup", + "packages": [{ "name": "node", "type": "formula" }], + "snapshot": { "...": "the snapshot you uploaded" }, + "visibility": "unlisted" } ``` --- -## Config Pages +## Config Endpoints (Public / CLI) -### Get Config JSON +### Get Config (CLI) -Get the full config data for a username/slug combination. +Returns a flattened, CLI-friendly shape used by `openboot install`. Different fields than `GET /api/configs/:slug`. ``` GET /:username/:slug/config ``` -**Auth required:** Only for private configs +**Auth required:** Only for `private` configs (Bearer token belonging to the owner). Public and unlisted configs are open. -**Response:** Config object (same as `GET /api/configs/:slug`) +**Response:** + +```json +{ + "username": "alex", + "slug": "my-setup", + "name": "My Dev Setup", + "preset": "developer", + "packages": [{ "name": "node", "desc": "JavaScript runtime" }], + "casks": [{ "name": "visual-studio-code", "desc": "Code editor" }], + "taps": ["homebrew/cask-fonts"], + "npm": [{ "name": "typescript", "desc": "..." }], + "dotfiles_repo": "", + "post_install": ["mkdir -p ~/projects"], + "macos_prefs": [ + { "domain": "com.apple.dock", "key": "tilesize", "value": "48", "type": "int" } + ] +} +``` + +`post_install` is the `custom_script` split into trimmed non-empty lines. ### Get Install Script -Get the shell install script for a config. +Get the shell install script for a config. The CLI's `install.sh` curls this URL. ``` GET /:username/:slug/install ``` -**Auth required:** Only for private configs (redirects to browser auth flow if unauthenticated) +**Auth required:** Only for `private` configs. Send a Bearer token belonging to the owner; otherwise the endpoint returns `403 Config is private` as plain text. (The browser-friendly auth flow for private configs is served via the curl-detection path at `/:username/:slug`, not here.) -**Response:** Shell script (`Content-Type: text/x-shellscript`) +**Response:** Shell script, `Content-Type: text/plain; charset=utf-8`. ```bash #!/bin/bash @@ -220,7 +305,7 @@ GET /:username/:slug/install ### List All Packages -Returns the complete package catalog with metadata. Used by the CLI to fetch package descriptions and installer types. Responses are cached (1h client, 24h CDN). +Returns the complete package catalog with metadata. Used by the CLI to fetch package descriptions and installer types. Cached `1h` client / `24h` CDN. ``` GET /api/packages @@ -271,7 +356,7 @@ GET /api/packages ### Search Homebrew Packages -Search for Homebrew formulae and casks. +Search for Homebrew formulae and casks. Results are limited to 30. ``` GET /api/homebrew/search?q= @@ -280,24 +365,24 @@ GET /api/homebrew/search?q= **Auth required:** No **Query parameters:** -- `q` — Search query +- `q` — Search query (min 2 chars) **Response:** ```json { - "formulae": [ - { "name": "node", "desc": "JavaScript runtime" } - ], - "casks": [ - { "name": "visual-studio-code", "desc": "Code editor" } + "results": [ + { "name": "node", "desc": "JavaScript runtime", "type": "formula" }, + { "name": "visual-studio-code", "desc": "Code editor", "type": "cask" } ] } ``` +When the query is too short or rate-limited, the response is `{ "results": [], "error": "..." }`. + ### Search NPM Packages -Search for npm packages. +Search for npm packages (proxies the npm registry, up to 30 results). ``` GET /api/npm/search?q= @@ -306,14 +391,14 @@ GET /api/npm/search?q= **Auth required:** No **Query parameters:** -- `q` — Search query +- `q` — Search query (min 2 chars) **Response:** ```json { - "packages": [ - { "name": "typescript", "description": "TypeScript language" } + "results": [ + { "name": "typescript", "desc": "TypeScript language", "type": "npm" } ] } ``` @@ -324,7 +409,7 @@ GET /api/npm/search?q= ### Parse Brewfile -Parse a Brewfile and extract package lists. +Parse a Brewfile and extract package lists. Used by the dashboard's Brewfile import. ``` POST /api/brewfile/parse @@ -332,24 +417,29 @@ POST /api/brewfile/parse **Auth required:** No -**Request body:** +**Request body:** JSON -``` -brew "node" -brew "go" -cask "visual-studio-code" +```json +{ + "content": "brew \"node\"\nbrew \"go\"\ncask \"visual-studio-code\"" +} ``` +Max content length: 50 KB. + **Response:** ```json { - "formulae": ["node", "go"], - "casks": ["visual-studio-code"], - "taps": [] + "packages": ["node", "go", "visual-studio-code"], + "formulas": ["node", "go"], + "casks": ["visual-studio-code"], + "taps": [] } ``` +`packages` is the combined `formulas + casks` list for convenience. Note the field is `formulas`, not `formulae`. + --- ## Authentication @@ -368,11 +458,11 @@ GET /api/user ```json { - "id": "usr_abc123", - "username": "johndoe", - "email": "john@example.com", - "avatar_url": "https://...", - "oauth_provider": "github" + "user": { + "id": "usr_abc123", + "username": "johndoe", + "email": "john@example.com" + } } ``` @@ -380,31 +470,29 @@ GET /api/user ## Rate Limits +Rate limits are enforced in-memory per Cloudflare Worker isolate (not globally consistent — see `src/lib/server/rate-limit.ts`). + | Endpoint Type | Limit | -|--------------|-------| -| Config reads | 60 requests per minute | -| Config writes | 20 requests per minute | -| Package search | 30 requests per minute | +|---------------|-------| +| Config reads (`/api/configs`, `/api/configs/:slug`) | 30 requests per minute | +| Config writes (POST/PUT/DELETE configs, snapshot upload) | 30 requests per minute | +| Search (`/api/homebrew/search`, `/api/npm/search`, `/api/brewfile/parse`) | 30 requests per minute | +| Auth login | 10 requests per minute | +| CLI device flow (start / approve / poll) | 5–20 requests per minute | -Rate limit headers are included in responses: - -``` -X-RateLimit-Limit: 60 -X-RateLimit-Remaining: 45 -X-RateLimit-Reset: 1234567890 -``` +When the limit is exceeded, the response is `429 Too Many Requests` with body `{ "error": "Rate limit exceeded" }` and a `Retry-After` header (seconds). No `X-RateLimit-*` headers are returned. --- ## Visibility Modes -Configs have three visibility levels that control access: +Configs have three visibility levels: -| Visibility | Config Page | Install URL | API Access | -|------------|-------------|-------------|------------| -| **public** | Anyone can view | Anyone can install | Anyone can read | -| **unlisted** | Anyone with link can view | Anyone can install | Anyone can read | -| **private** | Owner only | Requires auth | Owner only | +| Visibility | Listing | Install URL | API Read | +|------------|---------|-------------|----------| +| **public** | Listed on `/explore` and your profile | Anyone can install | Anyone (via `/:username/:slug/config`) | +| **unlisted** | Not listed publicly | Anyone with the link can install | Anyone with the link | +| **private** | Owner only | Requires owner's Bearer token (403 otherwise) | Requires owner's Bearer token | For private configs, run `openboot login` first to authenticate before installing. @@ -412,12 +500,10 @@ For private configs, run `openboot login` first to authenticate before installin ## Error Responses -All errors return JSON with an `error` field: +All errors return JSON with an `error` field, except plain-text install endpoints which return text: ```json -{ - "error": "Config not found" -} +{ "error": "Config not found" } ``` **HTTP Status Codes:** @@ -426,11 +512,12 @@ All errors return JSON with an `error` field: |------|---------| | 200 | Success | | 201 | Created | -| 204 | No Content (successful deletion) | | 400 | Bad Request (invalid input) | -| 401 | Unauthorized (missing or invalid auth token) | -| 403 | Forbidden (no permission to access resource) | +| 401 | Unauthorized (missing or invalid auth) | +| 403 | Forbidden (no permission, e.g. private config) | | 404 | Not Found | +| 409 | Conflict (slug or alias already taken) | +| 413 | Payload Too Large (snapshot > 1 MB) | | 429 | Too Many Requests (rate limit exceeded) | | 500 | Internal Server Error | @@ -441,7 +528,7 @@ All errors return JSON with an `error` field: ### Create a Config ```bash -TOKEN=$(cat ~/.openboot/auth.json | jq -r '.token') +TOKEN=$(jq -r '.token' ~/.openboot/auth.json) curl -X POST https://openboot.dev/api/configs \ -H "Authorization: Bearer $TOKEN" \ @@ -450,8 +537,8 @@ curl -X POST https://openboot.dev/api/configs \ "name": "My Setup", "base_preset": "developer", "packages": [ - {"name": "node", "type": "formula", "desc": "JavaScript runtime"}, - {"name": "visual-studio-code", "type": "cask", "desc": "Code editor"} + {"name": "node", "type": "formula"}, + {"name": "visual-studio-code", "type": "cask"} ] }' ``` @@ -462,7 +549,7 @@ curl -X POST https://openboot.dev/api/configs \ curl "https://openboot.dev/api/homebrew/search?q=docker" ``` -### Get Config JSON +### Get Config JSON (CLI shape) ```bash curl https://openboot.dev/johndoe/my-setup/config diff --git a/src/docs/config-options.md b/src/docs/config-options.md index 3969397..ec99d74 100644 --- a/src/docs/config-options.md +++ b/src/docs/config-options.md @@ -58,7 +58,7 @@ Which preset to start from. The preset's packages are included unless individual - **Type:** string - **Options:** `"minimal"`, `"developer"`, `"full"` - **Required:** no -- **Default:** none (empty config) +- **Default:** `"developer"` ### `packages` @@ -71,12 +71,15 @@ The tools and apps to install. Stored as an array of package objects, each with Each entry is an object: `{ "name": "package-name", "type": "formula" | "cask" | "npm" | "tap" }`. Names must match Homebrew or npm package names exactly. Taps are added before formulae and casks are installed. +**Limits:** max 500 packages per config; name max 200 characters (alphanumeric, `.`, `_`, `-`, `@`, `/`). + ### `custom_script` Shell commands that run after all packages are installed. Use this for project-specific setup like cloning repos, generating SSH keys, or configuring services. - **Type:** string (newline-separated commands) - **Required:** no +- **Max length:** 10,000 characters - **Runs as:** `bash` (errors are logged but don't stop the install) ``` @@ -89,8 +92,10 @@ ssh-keygen -t ed25519 -C "dev@yourcompany.com" -f ~/.ssh/id_ed25519 -N "" Git URL to a dotfiles repository. OpenBoot clones it to `~/.dotfiles` and optionally symlinks with [GNU Stow](https://www.gnu.org/software/stow/). -- **Type:** string (Git URL) +- **Type:** string (HTTPS Git URL) - **Required:** no +- **Max length:** 500 characters +- **Must:** start with `https://`. `ssh://` and `git@` URLs are rejected. - **Example:** `"https://github.com/yourname/dotfiles.git"` See [Dotfiles & Shell](/docs/dotfiles-shell) for setup details. @@ -103,6 +108,8 @@ Snapshot data attached to this config (auto-populated when creating from `openbo - **Required:** no - **Usually:** managed automatically, not edited by hand +Each entry in `snapshot.macos_prefs[]` has the shape `{ domain, key, value, type?, host? }`. `host` selects the `defaults` scope: `""` (default) writes to the main domain, `"currentHost"` writes via `defaults -currentHost` (required for keys under `~/Library/Preferences/ByHost/`). + ### `alias` Short URL alias for easy sharing. If set, this alias becomes the primary way to install the config — for example, `openboot install my-setup` instead of `openboot install username/slug`. The alias also works as a short URL: `openboot.dev/my-setup`. @@ -111,7 +118,10 @@ When a user runs `openboot install `, the CLI checks aliases first before - **Type:** string - **Required:** no +- **Length:** 2–20 characters (after cleaning) +- **Allowed characters:** lowercase letters, digits, hyphens. Other characters are stripped automatically. - **Must be:** unique across all configs +- **Reserved:** `api`, `install`, `dashboard`, `login`, `docs`, `cli-auth`, `explore` ### `visibility` diff --git a/src/docs/personal.md b/src/docs/personal.md index d34e52d..d087735 100644 --- a/src/docs/personal.md +++ b/src/docs/personal.md @@ -30,7 +30,6 @@ OpenBoot handles everything, then drops you into a TUI. You pick the `developer` ✓ visual-studio-code · arc · orbstack ✓ Oh-My-Zsh configured (git, zsh-autosuggestions, zsh-syntax-highlighting) ✓ macOS preferences applied (Fast Key Repeat, Finder Path Bar, Show Hidden Files) -✓ Dotfiles cloned and linked ✨ Setup complete. Restart your terminal. ``` diff --git a/src/docs/snapshot.md b/src/docs/snapshot.md index 2513129..a6f7736 100644 --- a/src/docs/snapshot.md +++ b/src/docs/snapshot.md @@ -110,7 +110,7 @@ openboot snapshot --import https://example.com/snapshot.json openboot snapshot --import my-setup.json --dry-run ``` -Before installing, a review editor lets you deselect anything you don't want. If the snapshot is partial (some capture steps failed), you'll be warned. +Before applying, a review editor lets you deselect anything you don't want. If the snapshot is partial (some capture steps failed), you'll be warned. | Category | What's applied | |----------|----------------| diff --git a/src/docs/teams.md b/src/docs/teams.md index fee6c46..3cd75c9 100644 --- a/src/docs/teams.md +++ b/src/docs/teams.md @@ -97,7 +97,7 @@ The URL never changes. When the stack changes, update the config in the [Dashboa Want to show your tech stack to candidates? Add the config URL to your README or job posts. They see exactly what they'd be working with. -Set visibility to **unlisted** (URL works, but not listed publicly) or **private** (requires authentication) depending on your needs. See [Custom Configs](/docs/custom-configs) for details. +For team-shared configs, use **unlisted** — the URL works for anyone who has it, but the config doesn't appear in public listings. **Private** is owner-only (other authenticated users get a 403), so it's not a fit for teams. See [Custom Configs](/docs/custom-configs) for details. ## Multiple teams, one system diff --git a/src/docs/what-is-openboot.md b/src/docs/what-is-openboot.md index 27d11bd..7683b76 100644 --- a/src/docs/what-is-openboot.md +++ b/src/docs/what-is-openboot.md @@ -7,7 +7,7 @@ order: 1 # What is OpenBoot -Fresh Mac? Run one command and get everything installed—Git, Node, Docker, your shell config, dotfiles, macOS preferences. Then snapshot that setup and share it with your team. +Fresh Mac? Run one command and get everything installed — Git, Node, Docker, your shell config, dotfiles, macOS preferences. Then snapshot that setup and share it with your team. OpenBoot is an open-source CLI that automates the boring part of setting up a new Mac. It installs Homebrew packages, GUI apps, configures your shell, links dotfiles, and applies developer-friendly macOS settings. All in one run.