Imported from zzstoatzz/my-prefect-server (
AGENTS.md). Install upstream withnpx skills add zzstoatzz/my-prefect-server. Copyright stays with the author.
this file is a set of notes for us:
credentials — read this before assuming you have no access
- local tooling secrets live in
.envat the repo root (gitignored, never committed). copy.env.exampleand fill it in. the justfile hasset dotenv-load, so everyjustrecipe picks.envup automatically — you do NOT have "no credentials" just because nothing is exported in your shell. .envkeys:HCLOUD_TOKEN,POSTGRES_PASSWORD,AUTH_STRING,DOMAIN,LETSENCRYPT_EMAIL(+ optionalGRAFANA_DOMAIN). the live server isDOMAIN=prefect-server.waow.tech.AUTH_STRING(user:pass) is the Prefect API admin credential. The Zig server's Prefect-compatible BasicAuth is enabled via theprefect-authsecret; unauthenticated/api/*calls returnUnauthorized, while/ui-settingsreportsauth: "BASIC"for the bundled Prefect v2 UI login flow. UseAUTH_STRINGfor CLI/API queries.- query the live server with
just prefect <args>(e.g.just prefect flow-run ls); it injectsPREFECT_API_URL/PREFECT_API_AUTH_STRINGfrom.env. raw API:curl -H "Authorization: Basic $(printf "$AUTH_STRING" | base64)" https://$DOMAIN/api/.... - flow runtime secrets (
ANTHROPIC_API_KEY,TURBOPUFFER_API_KEY,CLOUDFLARE_API_TOKEN,TURSO_*) are NOT in.env— they're Prefect Secret blocks, injected into flow runs viajob_variables.envinprefect.yaml, resolved at worker startup bymps.secrets_plugin. flows needing explicit typed block access usemps.blocks.
cluster access
- the prod stack runs on a single-node k3s cluster in the
prefectnamespace. the local orbstack k8s context is not prod — verify context before acting. - kubectl uses
kubeconfig.yamlat the repo root (gitignored). the justfile exportsKUBECONFIG := source_directory()/kubeconfig.yaml, sojust status/just logs/ deploy recipes target prod only once that file exists. - if
kubeconfig.yamlis missing, restore it out-of-band rather than relying on ambient kubectl context.just kubeconfigcurrently depends on local terraform state, which may not be present. - node SSH access exists out-of-band. avoid committing host/IP/key details; check local operator notes or ask the user when needed.
conventions
- keep the primary checkout on
main. Temporary task branches/worktrees must be reconciled and removed when the task finishes; preserve unfinished ideas in docs and recoverable archives rather than leaving permanent sibling checkouts. - land production service source and configuration on
mainbefore rollout. Immutable release pins and wheels are expected; running code that exists only on an abandoned branch is not. - never use
piporuv pip— useuv add,uv sync, oruv run --with - use
jqfor JSON processing, not python - prefect docs are on disk at
~/github.com/prefecthq/prefect/docs— read before guessing - use justfile recipes instead of ad-hoc commands
- a state's
nameis independent of itstype— returnCompleted(name="Degraded", message=...)from a flow whose run did its job with a dead upstream, instead of a boolean or a silent swallow. it stays visible and filterable, and does not page.docs/prefect-patterns.md - retries belong on every task that touches the network:
retries=3, retry_delay_seconds=[2, 5, 10], retry_jitter_factor=1. nevertry/excepta transient error inside the task — catching it means the engine never retries. let it raise; decide at the join point whether a dead source degrades the run or fails it - push to configured remotes. This repo currently has
origin(tangled.org); if agithubmirror remote is present in a checkout, push that too. - after server restart, re-fetch kubeconfig with
just kubeconfig - most flow execution runs on the
home-poolprocess worker on the home box (heavypad) — a systemd unit polling the server outbound over Tailscale (deploy/home-worker/). thekubernetes-poolbullets below describe the retained-but-unused k8s fallback path (no k8s worker runs in normal operation), and the analytics paths are hostPaths under/home/stoat/prefect-analytics, not a k8s PVC. - Git-backed flow code is pulled at runtime; selected Gardener flows instead import modules from pinned mps wheels, including on
gardener-exe. Preserve the declared loading mode:pull: []plus a wheel needs a dotted entrypoint unless files are explicitly supplied by the worker. Seedocs/deployments-validation.md. - run
just hooksonce per clone.just validate-deploymentschecks code-delivery contracts; CI repeats it before registration. Never change entrypoint syntax to satisfy documentation formatting. A passing static check does not establish artifact availability or runtime readiness. - worker image is
prefecthq/prefect:3-python3.14-kubernetes(the-kubernetestag matters; uv is pre-installed) PREFECT_INTEGRATIONS_KUBERNETES_OBSERVER_NAMESPACES=prefectis what makes namespace-scoped RBAC work- kubernetes work pool base job template defaults namespace to
default— must beprefect - flow pods install deps via
uv run --with 'my-prefect-server @ git+https://github.com/...'in thecommandjob variable — this creates an ephemeral venv before pull steps run - per-deployment overrides (e.g.
--python 3.13for dbt compat) go inwork_pool.job_variables.command, not at the deployment root - requires-python is >=3.13 (not 3.14) so the transform flow can run dbt under python 3.13
- the hub dbt project runs in Dagster on HeavyPad (
deploy/dagster/); Prefect's transform flow only submits and awaits the Dagster run. Keep dbt asset execution and metadata in Dagster. analytics.duckdbis single-writer: every RW open must hold theanalytics-duckdb-writerglobal concurrency limit (limit=1) viamps.lock.analytics_write_slot— new write paths go throughmps.db._write_connor wrap the slot themselves. readers snapshot the file to/tmpinstead of locking
agent tooling
before hand-rolling curl — or saying "the MCP can't do X" and offering to
build a tool — grep the tool list of the two MCP servers in plugins/mps/.
pdsx does full record CRUD (not read-only); the prefect one is read-only by
scope, and mutations go through just prefect .... details and the
Cloud-vs-ours confusion: docs/agent-tooling.md.
costs
COSTS.md figures are mostly wrong today — prefix-match the service name, and
prefer declared ownership over inferred. background: the "known-wrong state" section of COSTS.md.
running flows ad hoc
- Flow files under
flows/are ordinary Python modules. Most have anif __name__ == "__main__"entrypoint, so they can be run directly for local/debug execution, e.g.uv run python flows/diagnostics.pyoruv run python flows/phi_atlas.py --dry-run. - Direct local execution is useful for debugging pure Python behavior, but be
careful with write paths: default local storage may be
/tmp, not the production analytics dir (/home/stoat/prefect-analyticson the home box), and flow code that callsSecret.load(...)still needsPREFECT_API_URL/PREFECT_API_AUTH_STRINGpointed at the live server. - To exercise the real production worker (home-pool), pull steps, job variables,
and injected secret-block values, run deployments through the
Prefect API:
just prefect deployment run 'diagnostics/diagnostics' --watchorjust prefect deployment run 'ingest/ingest' --watch. prefect.yamlis the source of truth for deployment schedules, triggers, parameters, and per-deployment job variables. Re-register withjust prefect deploy --allafter dependency or flow changes.
docs
- reference material lives in
docs/; the README's docs table is the index. post-mortems go indocs/incidents/; shipped design notes and build logs go indocs/archive/, never the repo root or this file
