Skip to content

Conventions

This file is the source of truth. apps/dbb-docs/src/content/docs/conventions.md is a symlink to it, and the docs site renders that page.

On dbb-server, ~ holds Code/ and _logs/, plus dotfiles. Repos live in ~/Code/<repo>. Name every file in _logs/ YYYYMMDD-<what>. Never write to /tmp. Tool state stays out of ~: Go is at ~/.local/go and GOPATH is ~/.local/share/go, set in ~/.profile. Browser captures go in <repo>/_artifacts/.

Host operational logs from systemd timers live in /var/log/dbb/, owned by daz, and are rotated by logrotate (30 days). ~/_logs/ is for dated one-off artifacts, not rolling service logs.

Clean up after yourself: delete one-off logs and scratch files when a job is done. Darren removed ~/_backups on 11 Nov 2026 and does not want that folder recreated. dbb-auth users-file backups are /home/daz/_backups-auth/dbb-guests/ (mode 700). Keep the newest 20.

Changes under platform/auth/** redeploy dbb-auth, because Coolify watches that path. Get Darren’s yes before merging those changes.

Never opt in to complexity. Opt out of a service or developer default only with a stated reason in the exceptions register.

Use the official image. Keep the tool’s native config in the repo. A wrapper image, a renderer, a translation layer, a custom schema, or a call into a service’s internals needs a row in that register.

Inside the dbb-platform repo only, an app keeps apps/<app>/Dockerfile, and that file is the Coolify build default. Project repos use the flat layout in Project monorepo layout.

scripts/defaults-first.sh checks the dbb-platform repo only and runs on its pre-push. It looks at files changed since origin/main. It fails when one of those files is a Dockerfile outside apps/<app>/Dockerfile, an entrypoint or wrapper script, or a docker exec coolify php call, and the path is not in the register. The failure names this section.

Exception Reason Paths
pgAdmin pgpass refresh The official image copies pgpass only while pgadmin4.db is first created. The compose command refreshes the mode-600 copy, then runs the image entrypoint. platform/compose/dbb-pgadmin.yml
MinIO on pgsty/silo MinIO, Inc. stopped publishing the community image. dbb-minio uses Pigsty’s maintained fork. platform/compose/dbb-minio.yml
coolify-admin.php Coolify 4.5.3 has no API for the commands that remain, so platform/coolify.sh still runs this file. env-b64 reads a service env value. GET env omits it without read:sensitive. set-postgres-ports writes ports_mappings. PATCH /databases/{uuid} rejects that field. sync-pgadmin writes the pgAdmin file mounts. Those compose mounts are read-only in the storage API, and database passwords are omitted. ensure-app-file-mount sets a persistent volume host_path, which the storage API does not accept. set-dockerimage-pack sets build_pack to dockerimage, which PATCH /applications/{uuid} does not accept. mark-app-file sets is_based_on_git on a file mount. copy-slack-webhook-to-app and slack-webhook-present read the Slack webhook, which GET /notifications/slack omits. send-slack-test sends the test notification. There is no API for that. platform/coolify.sh, platform/coolify-admin.php
dbb-auth secret fill Coolify’s image deploy has no container args, and the official Authelia entrypoint reads /config/configuration.yml. platform/deploy-auth.sh fills the three env references into the volume copy. platform/deploy-auth.sh
dbb-docs repo-root build context The docs site renders this file through a symlink. Coolify’s base directory is / so the symlink target is inside the Docker build context. The Dockerfile location is /apps/dbb-docs/Dockerfile. apps/dbb-docs/Dockerfile
AstraZeneca CLL WordPress volume sync The official wordpress image declares VOLUME /var/www/html. With no mount, every deploy gets a new anonymous volume. With a persistent mount, the official entrypoint copies core only while index.php is missing and will not replace a theme that is already there. astrazeneca-cll-website/docker/entrypoint.sh in craftapplied/astrazeneca-cll copies /usr/src/wordpress onto that mount, excluding wp-content/uploads, then execs docker-entrypoint.sh. astrazeneca-cll-website/docker/entrypoint.sh
AstraZeneca CLL on MariaDB The official WordPress image configures MySQL or MariaDB through WORDPRESS_DB_*. astrazeneca-cll-db-dev (jfgreo38y2dlxmyob8tn7flu) and astrazeneca-cll-db-staging (jpugp4pavyxvjqtyuamhhljs) are mariadb:12.3.3. The Postgres layout in this file is for our own databases. Coolify databases jfgreo38y2dlxmyob8tn7flu and jpugp4pavyxvjqtyuamhhljs
My Emissions WordPress volume sync The official wordpress image declares VOLUME /var/www/html. With no mount, every deploy gets a new anonymous volume. With a persistent mount, the official entrypoint copies core only while index.php is missing and will not replace a theme that is already there. docker/entrypoint.sh in craftapplied/myemissions copies /usr/src/wordpress onto that mount, excluding wp-content/uploads and wp-content/plugins, copies ajaxfooddatabase into wp-content/plugins, then execs docker-entrypoint.sh. Other plugin directories stay on the volume. docker/entrypoint.sh in craftapplied/myemissions
My Emissions on MariaDB The official WordPress image configures MySQL or MariaDB through WORDPRESS_DB_*. myemissions-db-dev (higsbrtelujbrsg9e9ssapvx) is mariadb:12.3.3. The Postgres layout in this file is for our own databases. Coolify database higsbrtelujbrsg9e9ssapvx
My Emissions dev and staging only Darren, 9 Nov 2026. Development is the WordPress site he pulls production into with WP Migrate. Staging stays empty. Production is not on dbb-server. Pull-request previews stay off, because another WordPress instance is the thing the frontend rule forbids. Coolify project 8sifav2uudfo7ezfj69nay5x
Go Straight dev image The official rust image has no pkg-config, libasound2-dev or libudev-dev, and the crate’s cpal and gilrs dependencies do not build without them. The compose file has an inline Dockerfile that starts FROM the pinned official image, adds those three packages, and creates uid and gid 1000 daz so volume ownership matches the host. gostraight-dev starts a throwaway container from that image for cargo fetch, the one step that has network, because Coolify cannot start a one-off service. platform/compose/project-gostraight-dev.yml, platform/gostraight/gostraight-dev, platform/deploy-gostraight.sh
Go Straight dev only Darren, 11 Nov 2026. project-gostraight is a local-only Rust repo for tests. It has a dev environment and no staging, production or preview. The server history was scrubbed the same day (colour tables are not in git). There is still no git remote. Coolify project zhn5cstkc4a6w4hsjb98jg0m
Devbox base image One shared image for project shells, outside apps/<app>/Dockerfile. No official image ships herdr, omp, Neovim, LazyVim, fnm, Node, and pnpm together, so the image starts from Ubuntu 24.04 and installs those tools at build time. The image tag is the freeze and rollback point. The entrypoint copies the LazyVim starter, a stub ~/.zshrc, and omp’s gateway models.yml (LiteLLM reports no tool support for the Morph routes, so omp needs the override; the file also clamps Morph’s unsupported minimal thinking level to low) into the home directory when a home mount hides them, and its dotfiles hook is empty because this repo has no dotfiles to install. zsh is the default shell; bash stays as a fallback. platform/devbox/Dockerfile, platform/devbox/entrypoint, platform/devbox/omp/models.yml
Devbox PostgreSQL client 18 Ubuntu 24.04’s archive snapshots an older postgresql-client. The shared image needs the same major as the official PostGIS 18 sidecar, so dbb-devbox:2026.11.4 (live since 12 Nov 2026) installs postgresql-client-18 from the official PGDG apt repository (apt.postgresql.org, suite noble-pgdg) after checking the signing key fingerprint B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8. No patch-version pin. platform/devbox/Dockerfile
Devbox egress proxy A devbox may reach the public internet, but not private or internal destinations. Docker has no domain deny-list, and a host firewall rule would touch the shared host. The devbox’s only network is a Docker --internal network created by the deploy script and joined through network_mode, because Coolify adds its own routed network to every compose service that does not set network_mode. A Squid sidecar on Canonical’s ubuntu/squid image (there is no Docker Official Squid image) holds an open-by-default policy with a deny-list in a Coolify file mount. The box holds no host secrets or tokens, so an allow-list is not required. platform/devbox/devbox is a docker exec wrapper that opens a shell in the box. platform/compose/oneisland-devbox.yml, platform/deploy-devbox.sh, platform/devbox/devbox

Hostnames are flat: <project>-<service>[-<env>].server.daz.dev. Production has no environment suffix. Pull-request previews come off dev only. A preview hostname includes the environment, pr-<n>.<app>-<env>.server.daz.dev, so it cannot be mistaken for production. A dev preview is pr-<n>.<app>-dev.server.daz.dev. The Authelia group is <app>, the same group as the dev host. dbb-docs still uses docs-pr-<n>.server.daz.dev, and AstraZeneca CLL still uses astrazeneca-cll-website-pr-<n>.server.daz.dev. Those templates were not changed.

Coolify has one Project per product. A project does not need all three environments. It picks one profile and records that profile in services.md:

  • full: dev > staging > production
  • lean: dev > production
  • internal: production only

Git branches and Coolify environments use the same names: dev, staging, and main. Do not name either one development. Coolify lowercases every environment name when it saves it, so capitalised names cannot exist. Hostnames and database names keep the slugs dev, staging, and production. On 13 Nov 2026 every Coolify environment named development was renamed to dev. That rename does not change stored domains, environment variables, or webhook URLs. Environments named production were left as they are. craftapplied/astrazeneca-cll still has a git branch named development. Rename that branch to dev only with Darren’s yes.

Decided 4 Nov 2026. In a project monorepo, each service is a top-level folder named like its Coolify resource without the environment: <app>-<service>/. There is no apps/ folder and no nesting. The Dockerfile is <app>-<service>/Dockerfile. In Coolify the base directory is / and the Dockerfile location is /<app>-<service>/Dockerfile, so a service can use shared root files such as the lockfile.

Example, craftapplied/renewablesunwind: renewablesunwind-api/, renewablesunwind-webapp/, renewablesunwind-db/, renewablesunwind-email/, renewablesunwind-media/. AstraZeneca CLL follows it: astrazeneca-cll-website/, astrazeneca-cll-cms/.

A single-app repo keeps its framework’s own layout with the Dockerfile at the root. Seraphiim’s Rails app and the My Emissions theme do this.

The apps/<app>/ layout applies to the dbb-platform repo only.

Shared services in the Platform project are dbb-<app>[-<role>], with no environment suffix. That project has one real environment, production. Examples: dbb-docs, dbb-transcribe-webapp, dbb-transcribe-db, dbb-litellm, dbb-litellm-db, dbb-postgres-previews.

Client and app projects keep <app>-<role>-<env>, for example seraphiim-inloop-db-dev or astrazeneca-cll-webapp-dev. The role names the job (webapp, cms, api, db), not the product. The engine and version go in the one-sentence description, for example “Development Postgres 17.8 database for the Seraphiim InLoop rewrite.”. Renames are display-name only; Coolify keys containers, volumes, and hostnames by uuid.

A devbox database sidecar is named <project>-db-devbox, for example oneisland-db-devbox. It is a compose sidecar of <project>-devbox, not a standalone Coolify database resource. Keep the compose volume key when renaming the sidecar: Coolify names the volume {service-uuid}_{volume-key}, and a new key would start an empty volume. Project-specific instance settings live on that project’s docs page, such as One Island.

Applies to new databases. The existing Seraphiim and Renewables UnWind databases stay as they are.

  • One database per app per environment, with backups per database.
  • One schema per owner, named by role: cms for Payload or another CMS, app for our own tables. Nothing goes in public; run REVOKE CREATE ON SCHEMA public FROM PUBLIC.
  • One login role per schema, named <app>_<schema> with underscores (for example astrazeneca_cll_app). It owns only that schema and has search_path set to it.
  • The app role may get read access on cms, never the reverse.
  • Each service gets its own DATABASE_URL.
  • pgAdmin lists every Coolify Postgres under its Coolify name, in a server group named exactly after its Coolify project (for example Seraphiim).

A project’s devbox sidecar is disposable and is not backed up, so it sits outside the “backups per database” rule above. The long-lived database on it is <project>_db_<env> (for example oneisland_db_dev). Each worktree gets <project>_db_<env>_<branch-slug>, with - in the slug turned into _, and that database is dropped with the worktree.

A preview does not get a database from Coolify. Do not point a preview at a dev or production database.

Pull-request databases live on one shared server, dbb-postgres-previews (postgis/postgis:18-3.7). The container hostname other apps use is dbb-postgres-preview. The database name is <app>_pr_<n>. <n> is the -pr-<n> suffix of COOLIFY_CONTAINER_NAME. Each app has a Postgres role that can create and use only its own <app>_pr_* databases. PostGIS is installed in the image and is not created in each database; the app runs CREATE EXTENSION postgis in its own migration. The server is not backed up.

The three database homes are an app’s own <project>-db-<env> (dev, staging, production), this shared preview server, and a devbox sidecar <project>-db-devbox. New work uses Postgres 18. Previews and devboxes are first. Each other server stays on its current version until Darren says yes. The page is Databases.

The app’s preview environment is one shared set: PREVIEW_DB_HOST, PREVIEW_DB_PORT, PREVIEW_DB_USER, and PREVIEW_DB_PASSWORD. The app builds DATABASE_URL from that set plus the suffix. Its Coolify post-deploy command runs platform/preview-db-provision.sh (or the same one psql call), then the app’s own migrations and seed. platform/deploy-preview-db.sh --check is the host preflight. platform/preview-db-provision.sh --check is the in-container preflight. platform/preview-db-prune.sh drops a database when platform/preview-db-repos.conf names that app’s repository and gh reports the pull request closed or merged. An app that is not in the map is left in place. See platform/data.md.

Later, and not the default: an opt-in copy of dev, and a masked clone of live data using an open-source masking tool.

Access tiers:

  • Tailscale-only. Default for internal services.
  • Public behind sign-in. An app’s dev host, its pull-request previews, and its staging host. Grants and hostname patterns are in platform/auth.md.
  • Public. Production, or the app’s own login. Production is not a dbb-auth grant.

Code lives at ~/Code/<repo> on every machine. This repo is dbb-platform at ~/Code/dbb-platform. From the repo root, pnpm install installs the workspace and pnpm build runs each app’s build.

Use the fastest stable tool for the language, and only that tool.

TypeScript and JavaScript:

  • Lint with Oxlint. pnpm lint runs it from the repo root. The config is .oxlintrc.json.
  • Format with Biome. pnpm format writes changes. pnpm format --check checks them and does not write. The config is biome.json. Biome’s linter is off, so Oxlint is the only linter. The style is double quotes, semicolons, trailing commas, 2 spaces, and LF.
  • oxfmt can replace Biome when it is stable. It is still pre-1.0, so Biome stays the formatter.
  • Typecheck with tsgo from @typescript/native-preview. pnpm typecheck runs tsgo --noEmit for each workspace package that has a tsconfig.json. Those packages are apps/dbb-docs, apps/dbb-guests, and apps/dbb-transcribe. tsgo checks .ts files (and astro.config.mjs, which uses @ts-check). It does not typecheck .astro files, so Astro and Starlight frontmatter is outside this check. tsc stays off this path.
  • The package manager is pnpm. Install Node with fnm.
  • Use Vite to bundle and Vitest to test when an app needs a bundler or tests. dbb-docs builds with Astro, which uses Vite.

Oxlint (oxlint), Biome (@biomejs/biome), Lefthook (lefthook), and tsgo (@typescript/native-preview) are pinned devDependencies of the workspace root. They are not dependencies of an app, so an app image does not install them. Generated dist/, .astro/, and node_modules/ are ignored.

Lefthook runs the checks in lefthook.yml. pnpm install runs scripts/install-git-hooks.sh, which sets this worktree’s core.hooksPath to scripts/git-hooks. In CI, and when the directory is not a git checkout, that script prints one line and exits 0. An app image installs from the app directory, so this root prepare script does not run there.

scripts/git-hooks/pre-commit and scripts/git-hooks/pre-push run scripts/run-lefthook.sh. That script sources scripts/resolve-toolchain.sh before Lefthook starts. The resolver keeps node and pnpm from PATH when both are already there. Otherwise it uses fnm’s default alias. On this host that directory is ~/.local/share/fnm/aliases/default/bin. In the devbox image it is /usr/local/fnm/aliases/default/bin. If neither directory has both binaries, the hook prints the paths it checked and exits 1. It does not skip.

Pre-commit runs on staged files, in parallel. Biome checks formatting (biome ci with the linter and assist off). Oxlint lints. Commit does not typecheck.

Pre-push runs on the files in scripts/push-files.sh, in parallel. That list is the diff from the branch upstream to HEAD, or from origin/main when the branch has no upstream.

  • Biome and Oxlint, the same checks as pre-commit.
  • scripts/typecheck.sh with those files. pnpm typecheck still checks every package. The hook checks each app that contains a changed file. A change under packages/, or a file that is not inside a package, checks every package. The checker is tsgo. It has no partial file list, so the unit is the package tsconfig.json.
  • bash -n on each changed *.sh file.
  • shellcheck on those files when shellcheck is installed. When it is not, the hook prints a warning and continues.
  • scripts/changelog-pre-push.sh and scripts/defaults-first.sh on every push.

Run a hook by hand with sh scripts/run-lefthook.sh pre-commit or sh scripts/run-lefthook.sh pre-push. That command resolves node when the shell has no login PATH. pnpm exec lefthook run pre-commit and pnpm exec lefthook run pre-push are the same commands when node and pnpm are already on PATH.

Do not pass --no-verify to git commit or git push. Fix the failing check. Do not run lefthook install. It replaces the scripts in scripts/git-hooks.

Use Vitest for a new JS or TypeScript service. Keep a Jest setup that already works. Ordinary tests run on every pull request (in CI, once it exists).

Mutation testing is planned, not adopted yet. When it is, use StrykerJS on top of the service’s Vitest or Jest runner:

  • Mutate the logic that matters most, such as access control and business rules. Exclude generated code.
  • Run mutation testing on demand or on a schedule, not on every pull request.
  • Measure a baseline score first. Set a minimum only after that, and raise it over time.

Frontend and theme work is tested on the app’s dev environment. Merge into the dev branch, deploy to <app>-website-dev, and verify in a real browser.

Do not spin up a throwaway or cloned WordPress instance or database for that work.

Dev is fine to deploy to freely. Staging needs Darren’s explicit yes each time. Production rules stay as they are.

CI for lint, typecheck, build, and tests is not set up yet. When it is, run those as parallel jobs. Test only affected packages (pnpm --filter or Turborepo), and run the full suite nightly on main. For NestJS and Vitest, set isolate: false with a per-file state reset hook.

The changelog check’s CI command is scripts/changelog-ci.sh. Set CHANGELOG_BASE to the pull request base SHA, or the previous SHA of a push. It runs the same scripts/changelog-check.sh as the pre-push hook. Call it from GitHub Actions on pull requests and pushes, with permissions: contents: read and a full checkout. Adding that workflow file needs a credential that can update .github/workflows.

Rust:

  • Format with rustfmt. Lint with Clippy.
  • Pin the toolchain in rust-toolchain.toml.
  • Build in Docker. The image is the build environment.

Python, if a service ever needs it:

  • Use uv for the environment and dependencies.
  • Use Ruff to lint and format.

A check uses the shell, jq, rg, and wp (or platform/wordpress/dbb-wp-cli). rg is ripgrep, installed from Ubuntu’s ripgrep package. Python stays on the standard library. A script that needs another package runs with uv run --with <package>, or with inline PEP 723 metadata. uv is installed for the user in ~/.local/bin. A browser, axe, or anything of that weight runs in a Docker container. The host does not get system pip or npm install -g. A one-off script is not left on the host.

bootstrap/host-tools.sh installs both. ~/.local/bin is already on PATH for a login shell and for cursor-worker.service, so the uv installer does not edit the shell profile.

Other languages use the fastest standard tool for that language.

Layout:

  • apps/dbb-docs is the Starlight site, package dbb-docs. It was the dbb-os repository. It is an internal service at dbb-os.server.daz.dev.
  • apps/dbb-reviewer is planned, package dbb-reviewer. No code yet.
  • apps/dbb-logger is planned, package dbb-logger. No code yet.
  • packages/dbb-control is the dbb-control CLI. See dbb-control.
  • skills/ holds Darren’s agent skills. .cursor/skills in this repo is a symlink to skills/, so a checkout loads those skills in Cursor. Machine-wide skills are the synced directory in Agent skills.
  • bootstrap/ holds machine setup: playwright.sh for headless Chromium, host-tools.sh for ripgrep and uv, and rex.sh for the personal Rex server.
  • platform/ holds the shared data layer and Coolify alerts: compose files, provisioning scripts, platform/data.md, platform/alerts.md, and platform/deploy-review.md.

Each app is its own Coolify resource. The base directory is /apps/<dir> (/apps/dbb-reviewer, /apps/dbb-logger). Coolify uses that directory as the Docker build context. The Dockerfile lives in the app directory, and the Dockerfile location is /Dockerfile (Coolify joins it onto the base directory). Watch paths are apps/<dir>/**, so a commit that does not touch that app does not deploy it.

dbb-docs opts out of that base directory. Coolify’s base directory is /, and the Dockerfile location is /apps/dbb-docs/Dockerfile. Watch paths are apps/dbb-docs/** and conventions.md. The build context is the repo root so this file is in the image. dbb-docs listens on port 80. The reason is in the exceptions register.

Because the build context is the app directory, dbb-docs keeps apps/dbb-docs/pnpm-lock.yaml. pnpm-workspace.yaml sets sharedWorkspaceLockfile: false, and pnpm install at the repo root updates each app lockfile. The root pnpm-lock.yaml records the workspace root, including the pinned Oxlint, Biome, Lefthook, and tsgo devDependencies. pnpm-workspace.yaml sets allowBuilds.lefthook so pnpm runs Lefthook’s install script.

Agents check a preview in headless Chromium before handing work over. See Browser use.

Never write to /tmp. Secrets live in 1Password and in Coolify. Never print token values.

Agents call the Coolify API at http://127.0.0.1:8000. Do not use localhost: it resolves to ::1, which the allowlist rejects. The single token ~/.config/dbb/coolify-token has read, write, and deploy. The Coolify MCP server is disabled. Token names, paths, expiry, and the allowlist are in skills/coolify/SKILL.md. Cursor loads that skill through .cursor/skills.

Decided 15 Nov 2026. One directory on each machine holds the skills every tool loads. The default path is ~/.local/share/dbb-skills.

Darren’s own skills live in skills/ in this repo. An app repo keeps its skills in that repo’s .cursor/skills. platform/skills/sync-skills.sh does not read app repos.

skills.lock pins outside packs. pstack is https://github.com/cursor/plugins at pstack/skills, commit 076f4b2f57844ff9eb4ada914d4763884d40146e. cursor-team-kit is the same repo at cursor-team-kit/skills, commit ec569379616a4826b98f0f0a205dd3411b573611, and lists deslop, control-cli, and control-ui. The devbox image does not bake those packs. The compose file bind-mounts /home/daz/.local/share/dbb-skills read-only at /opt/dbb-devbox/skills-host. The entrypoint points omp, Grok Build, and Cursor at that mount. It does not replace ~/.omp/agent/skills and it does not write PSTACK_VERSION. On the host, --link still points ~/.omp/agent/skills at the shared directory. Inside a devbox, leave that path alone so a user’s own skills stay visible. The same compose file bind-mounts /home/daz/.local/share/dbb-tools read-only at /opt/dbb-devbox/dbb-tools and puts that bin directory on PATH, so dbb-control works in the box. Auth env for dbb-control is not passed in. The live box is dbb-devbox:2026.11.6. Rollback is 2026.11.5. Run platform/skills/sync-skills.sh on dbb-server so the skills mount is not empty, and keep the tools pack in place with platform/dbb-tools/publish-dbb-control.sh.

To bump a pin, change the commit in skills.lock and open a pull request. Then run the sync on each machine.

platform/skills/sync-skills.sh reads the lock, fetches each commit into ~/.cache/dbb-skills, and copies the allowed skills and skills/ into the destination. It writes the new tree beside the destination and renames it into place. A skill in skills/ replaces a pack skill of the same name, and the script prints the name. The destination MANIFEST records each pack, commit, skill name, and the count.

Terminal window
platform/skills/sync-skills.sh --dry-run
platform/skills/sync-skills.sh
platform/skills/sync-skills.sh --check
platform/skills/sync-skills.sh --link

--dest DIR sets the directory. --dry-run updates the cache and prints the plan. It leaves the destination and the tool links alone. --check prints each pinned commit next to the installed commit, the skill count, and each tool link. --link points these directories at the destination.

  • ~/.grok/skills
  • ~/.cursor/skills
  • ~/.omp/agent/skills
  • ${DSH_HOME:-~/.dsh}/skills, only when that DSH home already exists

When the path is a real directory, --link moves it to <dir>.bak-YYYYMMDD and prints the backup path. When that backup name already exists, the script adds -<n> instead of replacing it. A second run that already points at the destination changes nothing.

packages/dbb-control is the CLI agents use to drive a web app on this platform. Run it from the app repo.

dbb-control doctor checks Chromium, the optional config, and whether the target answers. dbb-control health requests the health and ready paths. open, shot, record, trace, console, and network open a route in headless Chromium. signin calls the sign-in function from the config. signin --api <api-base> signs in through the API’s email-OTP endpoints, saves the session for later calls and screenshots, and does not open a browser. seed, reset, and proof upload change data, so they require --yes. --dry-run prints the plan and does not run it.

The CLI prints JSON. --format text prints lines. --help on a command lists its flags.

An app can add dbb-control.config.ts at the repo root. The CLI walks up from the current directory and stops at the git root. The file default-exports defineConfig(...) with the app name, environment URLs, health paths, a sign-in function, seed and reset commands, and extra commands. With no file, pass an absolute URL to open, shot, record, trace, console, or network.

An env URL may be a string or an object of named bases such as { web, api }. A string is the web URL. The default base is web, or the only name. --url-name selects a named base. pathsHealth entries may be a path, or { path, urlName } so health can probe the API. Custom commands receive urls.

Preview URLs use https://pr-<n>.<app>-dev.server.daz.dev. <app> is nameApp in the config. envs.preview can replace that pattern, as a string or as named templates. A template includes {n} and may include {app}.

To install the CLI without cloning this repo, run platform/dbb-tools/publish-dbb-control.sh on dbb-server. It packs the package into ~/.local/share/dbb-tools and writes ~/.local/share/dbb-tools/bin/dbb-control. That launcher resolves its own install path, so dbb-control works from any directory, including a worktree. The CLI also resolves import { defineConfig } from "dbb-control" in an app config from the installed package. The devbox compose mounts that directory read-only at /opt/dbb-devbox/dbb-tools and puts bin on PATH. Do not treat a publish as a devbox deploy or restart, and do not pass DBB_CONTROL_AUTH_* into the box until Darren says so.

Dev, staging, and preview hosts use dbb-auth. The CLI reads DBB_CONTROL_AUTH_USER and DBB_CONTROL_AUTH_PASSWORD, or DBB_CONTROL_AUTH_SESSION for an existing dbb_auth_session cookie. It does not print those values. On dbb-server, source ~/.config/dbb-control/env first. That file uses the bot dbb-auth user (see Preview sign-in / platform/auth.md) and reads the password from /home/daz/_secrets/dbb-auth-bot.txt at source time. signin --api also reads DBB_CONTROL_SIGNIN_OTP and, when --as is not an email, DBB_CONTROL_SIGNIN_EMAIL. It does not print those values either.

proof upload reads DBB_CONTROL_MINIO_ENDPOINT, DBB_CONTROL_MINIO_ACCESS_KEY, DBB_CONTROL_MINIO_SECRET_KEY, and DBB_CONTROL_MINIO_BUCKET. DBB_CONTROL_MINIO_REGION defaults to us-east-1. The shareable URL is a presigned GET that lasts 7 days. The URL includes the access key id, which is how S3 presigning works. The secret key stays in the environment.

Captures go in <repo>/.dbb-control/. That directory is gitignored. The CLI does not write to /tmp. When TMPDIR is unset or is /tmp, the process uses <repo>/.dbb-control/scratch.

The cursor-team-kit skills control-cli and control-ui are markdown instructions for a one-off harness. They ship no binary and no package. Use dbb-control for that job.

One Island does not ship a config yet. Add one in that repo when you want its sign-in and seed commands wired in.

Every repo keeps a root CHANGELOG.md in Keep a Changelog 1.1.0.

  • The file has an # Changelog title and links to https://keepachangelog.com/en/1.1.0/.
  • ## [Unreleased] is the first version heading. Each change adds one line there.
  • A real release is ## [x.y.z] - YYYY-MM-DD. This repo has no releases. When an agent merges to main, it renames [Unreleased] to that day’s date, ## [YYYY-MM-DD] (or merges it into the section for that day), and adds a fresh ## [Unreleased] above it.
  • Version sections are newest first. On one day, the higher version comes first.
  • Groups are Added, Changed, Deprecated, Removed, Fixed, and Security. Use a group only when it has an entry. Empty groups are not required.
  • Do not invent a version, a date, or an entry. Unreleased work stays under [Unreleased] until a release exists. Historical wording stays as it was.
  • A push or pull request that changes code or config must change CHANGELOG.md in that same comparison. Markdown (*.md), paths under docs/, and generated trees (dist/, .astro/, node_modules/, coverage/, tmp/, log/, storage/, public/assets/, public/vite/, public/packs/, source maps, and minified files) do not.
  • The comparison is the triple-dot diff git diff <base>...<head>. <base> is the pull request base, or the remote tip of the branch before this push. Commits already on <base> are outside the check, so older history does not fail a new change.
  • scripts/changelog-check.sh --base <base> runs the check. The pre-push hook runs scripts/changelog-pre-push.sh, which chooses that base. CI runs scripts/changelog-ci.sh with that base in CHANGELOG_BASE. scripts/changelog-check-test.sh is the fixture test.
  • craftapplied/renewablesunwind branch main is the legacy WordPress theme. Do not add this changelog there, and do not change that theme to satisfy the check. dev and grokbot/port-nestjs-fastify carry it. Other branches get it when they are updated from those branches. Do not merge unrelated work to spread it.
  • Before a deploy, follow platform/deploy-review.md. The branch being deployed has to pass this check.

Destructive steps need Darren’s explicit yes before they run: wipes, DNS changes, opening ports, and deleting resources.

The service register is the repo file services.md.