원클릭으로
devrouter
Work with devrouter for local dev routing (HTTP + TCP/Postgres + dependency-only Docker services)
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Work with devrouter for local dev routing (HTTP + TCP/Postgres + dependency-only Docker services)
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Change the KlickerUZH Prisma schema, write migrations, or update seed data. Use when editing packages/prisma schema files, adding models/fields/enums, creating typed Json fields, syncing the analytics Python schema, or when a schema change must reach GraphQL types and e2e fixtures.
Choose the right test level and verify changes before a KlickerUZH PR. Use when deciding what to test, running tests locally, interpreting test failures, or assembling pre-PR verification evidence (typecheck, build, targeted tests, browser screenshots) for any change in this repository.
Diagnose and repair a broken KlickerUZH development environment. Use on a fresh clone/worktree, when pnpm install/build/check or git hooks fail, when Docker infra or ports misbehave, when codegen artifacts are stale, when Hatchet-dependent features do nothing, or when the database needs (re)seeding. Every other klicker skill routes here on environment failure.
Keep the KlickerUZH engineering wiki (docs/) and custom skills (.agents/skills/) accurate and up to date. Use when a change alters behavior or documented behavior, when you discover a non-obvious pattern worth recording, when adding/renaming/removing wiki pages or skills, or when documentation/skills and code disagree.
Use when writing, reviewing, or executing batch database scripts (seeds, updates, cleanups) to enforce the Safe Mutation Protocol, dry runs, PII refusal, and double-dump validation.
Build or change UI in the KlickerUZH pages-router frontends (frontend-manage, frontend-pwa, frontend-control, auth). Use for new pages/components, styling, forms, i18n strings, wiring generated GraphQL documents, and the mandatory browser verification of any visible change. NOT for apps/chat (app-router island — see docs/chat-platform.md).
| name | devrouter |
| description | Work with devrouter for local dev routing (HTTP + TCP/Postgres + dependency-only Docker services) |
| user-invocable | false |
Local dev routing via a shared Traefik reverse proxy. Provides stable *.localhost hostnames for HTTP apps and TCP/Postgres multiplexing on shared ports (80, 443, 5432).
.devrouter.yml (single source of truth).~/.config/devrouter (never edit manually)..localhost (lowercase alphanumeric + hyphens only)..devrouter.yml entry schemaversion: 1
devrouter:
version: <semver> # required for devrouter -V / devrouter upgrade
project:
name: <string> # optional
apps:
- name: <string> # unique within repo
kind: app | dependency # optional, default: app
dependencies: # optional
- app: <other-name>
envMap: # optional; maps target env var name -> per-dep source var name
DATABASE_URL: <UPPER_DEP_NAME>_URL
# if kind=app:
host: <name>.localhost
protocol: http | tcp
runtime: host | docker | proxy
# if kind=app and runtime=proxy (protocol http or tcp):
upstream: 127.0.0.1:3000 # already-running port to route to; no lifecycle/deps
# Loopback (127.0.0.1/localhost) -> host.docker.internal (a published host
# port). A non-loopback name is passed verbatim and resolved over devnet —
# so a devcontainer container ON devnet (with a network alias) can be fronted
# by NAME with NO published host port: upstream: <alias>:3000. This is the
# collision-free way to run many apps at once (each its own *.localhost).
# upstream may use the ${WORKSPACE} placeholder (e.g. ${WORKSPACE}-app:3000)
# to target a per-workspace devcontainer alias — substituted with the resolved
# workspace token at runtime. See "Workspace isolation" below. Do NOT put
# ${WORKSPACE} in `host` (rejected); the host is auto-namespaced.
# Managed `ensure` requires every HTTP/TCP upstream to begin with the exact
# resolved workspace/project alias prefix before DevPod or route mutation.
#
# proxy + tcp (front a DB in an externally-managed container, e.g. a
# devcontainer's Postgres on devnet) — no per-DB host port:
# protocol: tcp
# tcpProtocol: postgres # selects shared entrypoint :5432
# upstream: <db-alias>:5432 # devnet alias of the DB container
# Requires `devrouter tls install` (SNI is read from the TLS ClientHello). Connect
# with direct-SSL so the ClientHello carries SNI, e.g.:
# psql "host=db.<app>.localhost port=5432 sslmode=require sslnegotiation=direct ..."
# if kind=app and runtime=host (protocol must be http):
hostRun:
command: <string>
cwd: <string> # relative to repo root, must not escape it
portTimeout: 120 # seconds, optional
strategy:
type: auto
denyPorts: [80, 443, 5432]
allowPortRange: '1024-65535'
# if kind=app and runtime=docker:
docker:
service: <string>
internalPort: <number>
composeFiles: [<string>] # relative to repo root
router: <string> # optional
# if kind=app and protocol=tcp:
tcpProtocol: postgres # required; runtime must be docker OR proxy
# if kind=dependency:
runtime: docker
docker:
service: <string>
composeFiles: [<string>] # relative to repo root
Validation rules:
kind=app: host must end with .localhostkind=app: runtime=host supports protocol=http onlykind=app: runtime=proxy supports protocol=http or protocol=tcp, requires upstream (host:port), and forbids hostRun/docker/dependencies (it only registers a route to an externally-managed upstream). protocol=tcp additionally requires tcpProtocol and TLS (devrouter tls install)kind=app: protocol=tcp requires runtime=docker (devrouter-managed container) or runtime=proxy (externally-managed upstream), plus a supported tcpProtocol (postgres/redis/mariadb/mysql)kind=dependency: must use runtime=docker and does not allow routed fields (host/protocol/tcpProtocol/hostRun/docker.internalPort/docker.router)healthcheck. docker compose up --wait blocks until healthy; without one, wait returns immediately.POSTGRES_USER=prisma, POSTGRES_PASSWORD=prisma, POSTGRES_DB=prisma and create a shadow database. devrouter injects per-dep {PREFIX}_URL / {PREFIX}_SHADOW_URL with these credentials.docker compose down -v).Example healthcheck:
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U prisma -d prisma']
interval: 5s
timeout: 3s
retries: 20
When a host app depends on a TCP Docker service, devrouter app run and devrouter app exec inject per-dep deterministic vars (where {PREFIX} = dep.name.toUpperCase().replace(/-/g, "_")):
| Variable | Value |
|---|---|
{PREFIX}_HOST | localhost |
{PREFIX}_PORT | random mapped port |
{PREFIX}_URL | protocol-specific URL (postgres, redis, mysql/mariadb) |
{PREFIX}_SHADOW_URL | postgres://prisma:prisma@localhost:<port>/shadow (postgres only) |
Host apps also receive PORT (random free port), HOSTNAME=0.0.0.0, HOST=0.0.0.0.
Config-level envMap on dependency references aliases per-dep vars to app-expected names (for example DATABASE_URL: DB_URL maps the per-dep DB_URL to DATABASE_URL).
Run several worktrees of one repo in parallel without host/route collisions. A workspace token spans the DevPod id, devrouter routes, ${WORKSPACE} proxy upstreams, and devcontainer aliases.
DEVROUTER_WORKSPACE may repeat the identity but cannot rename it. Ambiguous identities fail closed. The primary checkout remains non-namespaced.web.localhost → web.<ws>.localhost), ${WORKSPACE} in upstream is substituted with the token, and the docker router key is suffixed per workspace. Managed ensure rejects every HTTP/TCP proxy upstream outside that exact alias namespace before it mutates DevPod or routes. The runtime config is computed in memory only — the committed .devrouter.yml is never rewritten.web.<ws>.localhost) are not covered by the *.localhost wildcard; devrouter auto-extends the mkcert cert SANs for active hosts when TLS is enabled.${localEnv:DEVCONTAINER_COMPOSE_OVERLAY:docker-compose.default.yml}; custom repositories may keep another default overlay. Selecting .devcontainer/docker-compose.devrouter.yml for linked worktrees must pass WORKSPACE and DEVROUTER_WORKSPACE across the combined base/overlay config and bind-mount ${DEVROUTER_GIT_COMMON_DIR} to the same absolute app-container path. The app exposes ${WORKSPACE}-app; the proxy uses upstream: ${WORKSPACE}-app:<port>.setup, use ensure . for both primary and linked checkouts; never branch manually on checkout kind or use live verify as startup. stop . is non-destructive; stop . --delete is explicit exact-owner cleanup without worktree removal; and exec . -- <command...> runs one-shot commands only in the exact running DevPod. Never substitute raw devpod up, stop, or delete: they bypass devrouter's machine-global ownership lock. workspace up creates linked worktrees; destructive worktree removal and GC remain ledger-scoped. Dirty or locked full down fails before side effects.ensure executes an exact captured adapter snapshot. Default reuse includes command argv, workspace, and adapter SHA-256. Set DEVROUTER_PROCESS_FINGERPRINT_ENV only to comma-separated non-secret environment names whose values affect runtime identity; secret-like names are rejected and raw values are never persisted.present, missing, locked, or conflict. workspace gc is a dry-run report; --yes deletes only exact ledger-owned missing/prunable resources and their records. GC never removes Git worktrees, branches, or prune state. Git has no worktree-removal hook..devrouter.yml folder without .git.secretManager.command in .devrouter.yml (include trailing --). devrouter wraps commands and re-injects dep env vars after the SM boundary.secretManager.defaultEnv: optional fallback environment for {env} template in command string.{env} template placeholder: secretManager.command: "infisical run --env {env} --" resolved at runtime. --env <env> CLI flag overrides defaultEnv.secretManager:
command: infisical run --env {env} --
defaultEnv: dev
envMap on dependency references to alias per-dep vars to app-expected names:
dependencies:
- app: db
envMap:
DATABASE_URL: DB_URL
DIRECT_URL: DB_URL
SHADOW_DATABASE_URL: DB_SHADOW_URL
infisical run or doppler run in sh -lc unless shell expansion is strictly required.devrouter app exec <app> --yes -- infisical run --projectId <id> --env=<env> -- pnpm payload migratedevrouter app exec <app> --yes -- printenv DB_URL DB_HOST DB_PORT DB_SHADOW_URLdevrouter app exec <app> --yes -- doppler run -- pnpm payload migratePROD_DATABASE_URL) and map intentionally via envMap.devrouter app exec --shell -- "<single command string>" only when shell expansion is required.envMap fails fast when source var is missing so migrations do not run with partial mapping..devrouter.yml metadata devrouter.version aligned with the currently applied devrouter release.devrouter -V (shows installed CLI version, local repo version, and next upgrade target).devrouter upgrade to list available upgrade targets and devrouter upgrade <version> to print that target's Agent Adaptation Prompt from upgrade-prompts/<version>.md.devrouter repo agents (or devrouter init --write-agents --write-skill).devrouter doctor --repo ., devrouter app ls --repo ., one representative devrouter app exec flow, and devrouter ls.devrouter init [--write-agents] [--write-skill]: print AI onboarding prompt (non-mutating by default)devrouter -V [--repo .]: show installed CLI version, local repo version, and next upgrade targetdevrouter upgrade [version] [--repo .]: list upgrade targets or print target Agent Adaptation Promptdevrouter setup --yes [--repo .] [--json]: first-run machine setup plus structured diagnosticsdevrouter ensure [path] [--open] [--json]: canonical startup/reconciliation for primary and linked checkoutsdevrouter stop [path] [--delete] [--json]: stop the exact DevPod and remove exact routes; --delete explicitly deletes its ownership-proven data without removing the checkoutdevrouter exec [path] -- <command...>: literal one-shot command inside the exact running DevPoddevrouter up / devrouter down: start/stop shared Traefik routerdevrouter status: router/container/network/TLS healthdevrouter doctor [--repo .]: deep diagnostics (global + repo)devrouter ls: list active HTTP + TCP routesdevrouter open <name>: open HTTP route or print TCP connection hint (matches app name, then service/container/host identities)devrouter logs [-f]: Traefik access logsdevrouter tls install: install mkcert certs, enable HTTPS + TCP/SNIdevrouter repo init: create .devrouter.ymldevrouter repo inspect [--json]: inspect package, scripts, compose services, env names, devcontainer, devrouter config, and agent guidance for onboardingdevrouter repo devcontainer write --dry-run --json: plan conservative Node/pnpm/Postgres devcontainer/devrouter scaffold files without writingdevrouter repo devcontainer write --yes: write managed Node/pnpm/Postgres devcontainer/devrouter scaffold files when no custom-file conflicts existdevrouter repo devcontainer verify --json: emit read-only onboarding evidence for PRsdevrouter repo devcontainer verify --live --yes --json: deprecated compatibility verification after ensure; never use as startupdevrouter repo agents: write devrouter section in AGENTS.md + install this skilldevrouter app add: add/update app entry in .devrouter.ymldevrouter app ls: list app entriesdevrouter app run <name> [--env <env>] [--workspace <slug>]: run app with dependency lifecycle (--env overrides SM defaultEnv; --workspace overrides the per-workspace token)devrouter app exec <name> [--shell] [--env <env>] [--workspace <slug>] -- <cmd>: one-shot command with resolved dep envdevrouter app rm <name> [--keep-config]: remove app entry (--keep-config frees only the live route/hostname, leaves .devrouter.yml untouched)devrouter workspace up <branch> [--path <dir>] [--no-devpod] [--open]: create a worktree and start/prove it unless create-only mode is requesteddevrouter workspace ensure [path] [--open] [--json]: compatibility alias of devrouter ensuredevrouter workspace ls [--json]: list ownership, Git, DevPod, route, path, and branch evidencedevrouter workspace stop <workspace|branch>: stop DevPod and routes; preserve checkout, owner record, and datadevrouter workspace down <workspace|branch> [--keep-worktree]: delete runtime/routes and optionally remove the clean worktree and recorddevrouter workspace gc [--json] [--yes]: report missing owners by default; apply exact eligible cleanup with --yesFor devcontainer onboarding:
devrouter setup --repo . --yes --jsondevrouter doctor --repo . --jsondevrouter repo inspect --repo . --jsondevrouter repo devcontainer write --repo . --dry-run --jsondevrouter repo devcontainer write --repo . --yesdevrouter repo devcontainer verify --repo . --jsondevrouter ensure . --jsondevrouter exec . -- <command...>For host/docker runtime apps only:
devrouter setup --repo . --yesdevrouter doctor --repo .devrouter app ls --repo .devrouter app run <host-app> --repo . --yesdevrouter lscurl -I https://<host>.localhostdevrouter open <name> for the connection hint.devrouter ensure delivers its matching process helper to the exact running container and invokes the repository-owned .devcontainer/post-start.sh; keep .devrouter.yml as the only consumer-side devrouter version pin.devrouter app run auto-starts Docker dependencies and waits for health. Host app runs stop auto-started docker deps on exit; docker app runs leave target services running until explicit cleanup.kind=dependency entries do not create routes and cannot be direct targets for devrouter app run, devrouter app exec, or devrouter open.kind=dependency services start as declared in compose (no Traefik label wiring, no random port publishing, no injected env vars).:5432 requires TLS/SNI (devrouter tls install). Standard app clients should use the injected random port instead.devrouter app exec follows the same dep lifecycle for one-shot commands and preserves argv semantics by default (shell: false).devrouter app exec --shell is explicit and requires exactly one command string after --.printenv DB_URL DB_HOST DB_PORT) before migrate/seed.