Skip to content

configuration

every knob both services read. bootstrap is the order to set them in; this page is the reference.

spec board

core

var default what it does
NAMESPACES empty comma-separated allowlist of target repos (owner/repo). specs pointing outside it render but never open PRs
DEFAULT_NAMESPACE first of NAMESPACES project for specs whose frontmatter names none. must match the editor's CMD_SPEC_DEFAULT_NAMESPACE
HEDGEDOC_BASE_URL http://localhost:3000 where the editor is, for note links
HEDGEDOC_INTERNAL_URL HEDGEDOC_BASE_URL editor address used for signed board mutations; use an internal service address when available
SPEC_BOARD_BASE_URL empty the board's own public origin. email has no request to derive it from, so unset means no email
PORT 8080 listen port
PG* libpq defaults PGHOST, PGUSER, PGPASSWORD, PGDATABASE; the same database the editor uses
SPEC_TAG spec frontmatter tag that marks a note as a spec
POLL_SECONDS 60 poll interval. one tick at a time, under a postgres advisory lock
STALE_DAYS 14 days without a change before a reviewing card gets a stale marker
FETCH_TIMEOUT_MS 15000 hard deadline on every outbound call and pg query, so a hung socket cannot wedge the poll loop
TRUSTED_PROXIES 1 reverse proxies in front of the board. the rate limiter reads the caller's address this many hops from its own end of X-Forwarded-For, so a caller cannot pick its own bucket. one openshift route is 1; set 0 if nothing fronts the board, or a caller writes the header itself

board numeric settings reject non-finite, negative and out-of-range values at startup. ports are 1–65535, proxy counts 0–32, poll seconds 1–86400, fetch timeouts 1–300000 ms and overlap budgets 1–10000000 bytes. idle and debounce minutes accept zero and fractions up to 10080; stale days accept 0–365000.

github

var default what it does
GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY unset per-project installation tokens; preferred, and self-refreshing
GITHUB_TOKEN unset service PAT: resolves roles.yml, scans for implements commits, and backs the app up where it is not installed
SPECS_DIR specs fallback target dir for spec files. a project's specs-dir in roles.yml wins (onboarding)

without either credential the board still renders specs, but resolves no approvers and opens no PRs.

notifications and email

var default what it does
WEBHOOK_URL unset posts status moves, comments, approvals, locks, PRs, supersedes and implementations
SMTP_HOST unset enables digests, together with SPEC_BOARD_BASE_URL and SESSION_SECRET
SMTP_PORT 587
SMTP_SECURE false true for implicit TLS
SMTP_USER, SMTP_PASS unset omit for an unauthenticated relay
SMTP_FROM specdoc@localhost no-reply sender
EMAIL_DEBOUNCE_MINUTES 30 quiet period per recipient; each event resets it, and a burst collapses into one message
EMAIL_ORG_NAME SpecDoc sender identity in the mail footer
EMAIL_POSTAL_ADDRESS empty postal address in the footer, which bulk-mail rules expect
PRIVACY_URL the board's /privacy override for the data-handling notice linked from mail
PRIVACY_CONTACT SMTP_FROM address for data-handling requests, distinct from the no-reply sender

email refuses to start without a signable unsubscribe link (bootstrap explains the rotation cost).

settings page and review bots

var default what it does
BOARD_OAUTH_CLIENT_ID, BOARD_OAUTH_CLIENT_SECRET unset github oauth app for /settings and /bots
SESSION_SECRET unset signs board session cookies and unsubscribe tokens. required for both the settings page and email
EDITOR_SECRET unset shared with the editor's CMD_SPEC_BOARD_SECRET; verifies saved-version approval assertions and signs editor mutations. required for approvals, automatic locks and bot writes
BOARD_ADMINS empty comma-separated github logins allowed to manage review bots at /bots and cut checkpoints at /checkpoints
REVIEW_IDLE_MINUTES 10 quiet time before a bot reviews a note; the editor separately refuses writes while the note is open
OVERLAP_MAX_BYTES 200000 budget for the checkpoint overlap pass, which sends a project's whole approved corpus in one request. size it to the model's context

a bot itself lives in the database, one row per bot managed from /bots: name, openai-compatible endpoint, model, optional api key, prompt, and the projects it reviews. the review fingerprint includes prose, prompt, model, endpoint, the inherited top-level specs and which peer specs were sent, but not the peers' own text; changes schedule a fresh review within the poll budget. a peer's revision therefore does not re-review every spec that names it, and the conflict it introduces waits for that spec's own next edit or for the checkpoint overlap pass. most of its findings land as {>>@<name>: ...<<} threads that block approval until resolved; a conflict it reports with another spec is advisory and shows on the board card instead. troubleshooting is in operations.

the endpoint must support /v1/chat/completions with response_format: json_schema and return the final JSON in choices[0].message.content. the configured prompt applies to individual reviews; overlap checks, checkpoint summaries and implementation feedback use their own prompts. output limits are 3072 tokens for reviews, 4096 for overlaps, 2048 for summaries and 5000 for feedback, within a 120-second request deadline. reasoning models must leave room in these limits for the final JSON; cap thinking at the endpoint and return it separately from message.content.

size context for the task: reviews send up to 24000 characters plus 12000 of inherited context, while feedback accepts up to 160000 characters and overlaps use OVERLAP_MAX_BYTES. character and byte limits are not token counts. allow space for the prompt and output, and reject oversized requests rather than silently dropping source text.

implementation feedback

feedback-bot: <name> in a project's roles.yml selects an enabled bot assigned to that project. no selection means no collection or generation. with a bot selected, automatic spec amendment proposals is on unless a project approver or board admin turns it off in /settings. this shared project setting persists across restarts, separately from personal email preferences. it pauses automatic work and prevents an in-flight result from publishing after the setting changes. suggestions already in notes stay.

generation happens after a linked implementation PR merges. discovery starts with PRs updated within 30 days and reconciles that rolling window daily. tracked merged PRs refresh at most daily until 30 days after merge. an explicit import performs one fresh pass for an older merged PR. implementation PR descriptions carry the same implements reference syntax as commits.

feedback shares the existing four model calls per poll and takes at most one slot. one repository discovery step reads at most two pages; discovery and source work share a 64-request budget. discussion and file lists are capped at five pages each, collected evidence at 200 kB, and model input at 160,000 characters. incomplete input is reported for retry rather than treated as no findings.

a bot can also hold a token, issued, replaced and revoked from its panel at /bots; the board stores only a sha256 of it. the token authorizes the bot write api for the bot's projects, which is what mcp/'s write tools use to let a local model review as that bot. leave endpoint and model blank for a bot that exists only for this: the poller never sends such a bot anything.

specdoc-mcp

the agent-facing server in mcp/ (context graph for agents) is configured from the environment of the process that starts it, usually an .mcp.json in the implementation repo.

var default what it does
SPECDOC_URL https://specs.josie.cloud the board whose /api/specs it reads
SPECDOC_NAMESPACE derived comma-separated spec repos to serve. unset, it is the set named by the checkout's implements owner/repo#N commits, and everything the board serves when there are none
SPECDOC_REPO cwd the checkout to index
SPECDOC_MAX_TOKENS 1500 default response budget, 50 to 20000; each tool call can pass its own max_tokens
SPECDOC_BRIEF_TOKENS 1000 size of the file brief --out writes and the brief tool's default, same range
SPECDOC_BOT_TOKEN unset a bot token from /bots. set, it adds the write tools under that bot's name

editor

the editor is a hedgedoc 1.x derivative and takes upstream's CMD_* configuration unchanged. what a deployment has to set:

var what it does
CMD_DB_URL the shared database. the editor creates the schema the board reads, so start it first
CMD_DOMAIN, CMD_PROTOCOL_USESSL public hostname, used to build note URLs
CMD_SESSION_SECRET session cookies
CMD_GITHUB_CLIENTID, CMD_GITHUB_CLIENTSECRET github login. the fork asks for no repo scope: the editor only needs identity
CMD_IMAGE_UPLOAD_TYPE=filesystem uploads land on the RWO volume

these settings exist only in this fork:

var what it does
CMD_SPEC_BOARD_URL the board's public origin. allows it in the editor's CSP connect-src, so the approval widget can read project roles, and returns it as the CORS origin on /me. without it approvals never resolve
CMD_SPEC_BOARD_SECRET the board's EDITOR_SECRET. verifies signed board mutations and signs five-minute GitHub approval assertions bound to note/action/saved text; unset, those routes return 404
CMD_SPEC_DEFAULT_NAMESPACE project prefilled into the /new/spec template. must match the board's DEFAULT_NAMESPACE

compose.yaml is a complete working set of both.