{"id":50392910,"url":"https://github.com/leostera/gumgum","last_synced_at":"2026-05-30T19:01:50.807Z","repository":{"id":359665808,"uuid":"1246727132","full_name":"leostera/gumgum","owner":"leostera","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-22T22:36:44.000Z","size":6786,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T22:56:48.478Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leostera.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"leostera"}},"created_at":"2026-05-22T13:44:12.000Z","updated_at":"2026-05-22T22:37:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/leostera/gumgum","commit_stats":null,"previous_names":["leostera/gumgum"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/leostera/gumgum","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leostera%2Fgumgum","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leostera%2Fgumgum/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leostera%2Fgumgum/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leostera%2Fgumgum/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leostera","download_url":"https://codeload.github.com/leostera/gumgum/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leostera%2Fgumgum/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33705207,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T19:01:49.103Z","updated_at":"2026-05-30T19:01:50.800Z","avatar_url":"https://github.com/leostera.png","language":"Rust","funding_links":["https://github.com/sponsors/leostera"],"categories":[],"sub_categories":[],"readme":"# gumgum\n\ngumgum is a self-hosted app platform for your VPS or local machine. It gives small projects a Cloudflare-like workflow without handing the runtime to a hosted control plane: set up one server, deploy workers, attach databases/KV/buckets/queues/secrets, inspect logs and events, and publish routes from one CLI.\n\nLicense: GNU Affero General Public License v3.0 only (`AGPL-3.0-only`).\n\n## Table of contents\n\n- [Why gumgum?](#why-gumgum)\n- [Core concepts](#core-concepts)\n- [What gumgum manages](#what-gumgum-manages)\n- [Install the CLI](#install-the-cli)\n- [Set up a server](#set-up-a-server)\n  - [Local server](#local-server)\n  - [Remote server](#remote-server)\n  - [Inspect and remove servers](#inspect-and-remove-servers)\n- [Create a project or workspace](#create-a-project-or-workspace)\n  - [Create a workspace](#create-a-workspace)\n  - [Create workers](#create-workers)\n- [Manage domains and ingress](#manage-domains-and-ingress)\n- [Manage resources](#manage-resources)\n  - [Databases](#databases)\n  - [KV namespaces](#kv-namespaces)\n  - [Buckets](#buckets)\n  - [Queues](#queues)\n  - [Secrets](#secrets)\n- [Bind resources to workers](#bind-resources-to-workers)\n- [Observability and platform services](#observability-and-platform-services)\n- [Deploy](#deploy)\n- [Logs, environment, status, and events](#logs-environment-status-and-events)\n- [Graph inspection](#graph-inspection)\n- [Bucket object operations](#bucket-object-operations)\n- [Publish](#publish)\n- [Rollback and revisions](#rollback-and-revisions)\n- [Configuration](#configuration)\n- [JSON output](#json-output)\n- [End-to-end example](#end-to-end-example)\n- [Command reference](#command-reference)\n- [License](#license)\n\n## Why gumgum?\n\n- **One server, one simple platform.** Turn a Linux host or your local computer into a small PaaS.\n- **Batteries included.** gumgum starts the backing services most apps need: Postgres, Redis, MinIO/S3 buckets, Redpanda queues, secrets, and an observability stack.\n- **Project-first workflow.** Create resources, bind them to workers, deploy, view logs, and inspect events from the project directory.\n- **Local-first control.** The daemon runs on your server. Your CLI sends intent; gumgum handles runtime/provider convergence there.\n- **Human CLI by default.** Commands print readable output unless you ask for `--json`.\n\n## Core concepts\n\n- **Server**: the machine that runs `gumgumd`, Docker workloads, the gateway, and built-in providers. A server can have a public control-plane domain used for default routes.\n- **Domain**: a DNS zone or delegated domain GumGum may use for routes. Domains can be manual or Cloudflare-managed.\n- **Project**: one app/workspace with a production domain in `gumgum.toml`.\n- **Environment**: an isolated deployment target. GumGum uses `preview` and `prod`; app/provider containers and data objects are environment-scoped.\n- **Workspace**: a directory containing multiple worker projects.\n- **Resource**: a backing service object such as a database, KV namespace, bucket, queue, or secret.\n- **Binding**: an environment projection from a resource into a worker, for example `DATABASE_URL` or `UPLOADS_BUCKET`.\n- **Desired state**: the resources, bindings, routes, and deployments gumgum should maintain on the server.\n\n## What gumgum manages\n\ngumgum can manage:\n\n- app/worker deployment to Docker\n- explicit HTTP routes through the gumgum gateway\n- Postgres databases on host volumes under `/gumgum/volumes/providers/*/postgres`\n- Redis KV namespaces on host volumes under `/gumgum/volumes/providers/*/redis` with append-only persistence\n- MinIO/S3 buckets and bucket objects on host volumes under `/gumgum/volumes/providers/*/minio`\n- Redpanda queues/topics on host volumes under `/gumgum/volumes/providers/*/redpanda`\n- platform secrets and observability services\n- Prometheus scraping, host metrics, Docker/container metrics, Grafana dashboards, Loki logs, Tempo traces, and OTEL collector plumbing\n- worker environment bindings\n- logs, events, grouped event summaries, rollback metadata, and publish previews\n\n## Install the CLI\n\nBuild the CLI from this repository:\n\n```bash\ngit clone https://github.com/leostera/gumgum.git\ncd gumgum\ncargo build --release\ninstall -m 0755 target/release/gumgum ~/.local/bin/gumgum\n```\n\nMake sure `~/.local/bin` is on your `PATH`:\n\n```bash\nexport PATH=\"$HOME/.local/bin:$PATH\"\n```\n\nCheck the CLI:\n\n```bash\ngumgum --help\ngumgum version\n```\n\n## Set up a server\n\n`gumgum server add` is the main setup command. It installs/configures `gumgumd`, starts built-in providers, and saves the server locally. GumGum upgrades also migrate the daemon's own internal SQLite graph database automatically before the daemon starts serving requests.\n\n### Local server\n\nUse this when you want to run apps on your own machine:\n\n```bash\ngumgum server add 0.0.0.0 --name local --domain example.dev\n```\n\nThis configures a local gumgum server named `local`. GumGum does not install local DNS resolver entries; use real DNS, `/etc/hosts`, or `curl --resolve` for local testing.\n\n### Remote server\n\nUse this when you want to run apps on another machine over SSH:\n\n```bash\ngumgum server add 203.0.113.10 --name prod --domain example.com\n```\n\nIf SSH needs a user:\n\n```bash\ngumgum server add 203.0.113.10 \\\n  --user root \\\n  --name prod \\\n  --domain example.com\n```\n\nPreview setup without changing anything:\n\n```bash\ngumgum --dry-run server add 203.0.113.10 --name prod --domain example.com\n```\n\n### Inspect and remove servers\n\nList configured servers:\n\n```bash\ngumgum server list\n```\n\nPing the daemon:\n\n```bash\ngumgum server ping --host prod\n```\n\nInspect daemon capabilities:\n\n```bash\ngumgum server capabilities list --host prod\n```\n\nRequire specific daemon capabilities:\n\n```bash\ngumgum server capabilities list --host prod \\\n  --require gumgum:events,gumgum:buckets:objects\n```\n\nRemove a server from your local config:\n\n```bash\ngumgum server rm prod\n```\n\nRemoving a server record does not delete remote containers or resources.\n\n## Create a project or workspace\n\n### Create a workspace\n\n`gumgum init` creates a workspace manifest in the current directory:\n\n```bash\ngumgum init --name my-app\n```\n\nUseful options:\n\n```bash\ngumgum init --name my-app --domain example.com\ngumgum init --name my-app --namespace production\ngumgum init --name my-app --force\n```\n\n### Create workers\n\nWorkers are managed with `gumgum worker`, separate from workspace initialization.\n\nCreate workers inside the current workspace:\n\n```bash\ngumgum worker create api --port 3000\ngumgum worker create jobs --port 3001\n```\n\nThis creates worker folders with their own `gumgum.toml` files and adds them to the workspace members list.\n\nChoose an explicit directory or zones:\n\n```bash\ngumgum worker create api --dir apps/api --port 3000\ngumgum worker create api --zone example.com --zone example.net\n```\n\nList workers:\n\n```bash\ngumgum worker list\n```\n\nRemove a worker from the workspace:\n\n```bash\ngumgum worker delete api\n```\n\n`worker delete` only removes the worker from the workspace manifest. It never deletes source files.\n\nWorkspace-aware commands such as `logs`, `env`, and `publish --dry-run` can operate across all workers from the workspace root.\n\n## Manage domains and ingress\n\nServers run the GumGum control plane; domains describe DNS zones that a server can use for routes. Add a domain after a server is available:\n\n```bash\ngumgum domain add leostera.dev --server starbase2 --provider cloudflare --ingress cloudflare\n```\n\n`--provider manual` records the domain and prints the desired intent without changing DNS. `--provider cloudflare` prompts for a Cloudflare API token and saves it on the GumGum server so future route convergence can manage DNS/tunnel state there.\n\nRequired Cloudflare token permissions:\n\n| Scope | Permission | Applies to |\n| --- | --- | --- |\n| Zone | DNS: Edit | Every domain GumGum should manage |\n| Zone | Zone: Read | Every domain GumGum should manage |\n| Account | Cloudflare Tunnel: Edit | Account used for Cloudflare Tunnel ingress |\n\nIf `Cloudflare Tunnel: Edit` is not available in your dashboard, use `Account / Zero Trust: Edit` as the broader fallback. You do not need Zone Access permissions unless you are separately creating Cloudflare Access apps/policies. Scoping the token to all zones is convenient when one GumGum server will host multiple domains; for stricter isolation, scope it only to domains GumGum should manage and update/recreate it before adding another domain.\n\nAs setup sugar, `gumgum server add ... --domain leostera.dev --ingress cloudflare` installs the server and then adds that domain with Cloudflare ingress.\n\nWhen a deployed worker has `public = true`, GumGum converges public ingress for its derived route. For a Cloudflare-managed domain with Cloudflare ingress, deploy ensures a proxied CNAME to the GumGum Cloudflare Tunnel and adds a tunnel rule to the server gateway.\n\n## Manage resources\n\nResource commands share a common shape:\n\n```bash\ngumgum \u003ckind\u003e list\ngumgum \u003ckind\u003e create \u003cname\u003e\ngumgum \u003ckind\u003e delete \u003cname\u003e --preview\ngumgum \u003ckind\u003e delete \u003cname\u003e\n```\n\n`\u003ckind\u003e` is one of:\n\n- `db`\n- `kv`\n- `bucket`\n- `queue`\n- `secret`\n\nPass `--host \u003cserver\u003e` when you want to target a specific server instead of the configured/default one.\n\n### Databases\n\nCreate and list Postgres databases:\n\n```bash\ngumgum db create app-db\ngumgum db list\n```\n\nDelete safely with a preview first:\n\n```bash\ngumgum db delete app-db --preview\ngumgum db delete app-db\n```\n\nCreate with an explicit namespace or root domain when needed:\n\n```bash\ngumgum db create app-db --namespace prod\ngumgum db create app-db --domain example.com\n```\n\n### KV namespaces\n\nCreate and list Redis-backed KV namespaces:\n\n```bash\ngumgum kv create app-cache\ngumgum kv list\n```\n\nDelete:\n\n```bash\ngumgum kv delete app-cache --preview\ngumgum kv delete app-cache\n```\n\n### Buckets\n\nCreate and list MinIO/S3-backed buckets:\n\n```bash\ngumgum bucket create uploads\ngumgum bucket list\n```\n\nDelete:\n\n```bash\ngumgum bucket delete uploads --preview\ngumgum bucket delete uploads\n```\n\nBucket object commands are covered in [Bucket object operations](#bucket-object-operations).\n\n### Queues\n\nCreate and list Redpanda-backed queues/topics:\n\n```bash\ngumgum queue create jobs\ngumgum queue list\n```\n\nDelete:\n\n```bash\ngumgum queue delete jobs --preview\ngumgum queue delete jobs\n```\n\n### Secrets\n\nCreate and list secrets:\n\n```bash\ngumgum secret create api-token\ngumgum secret list\n```\n\nDelete:\n\n```bash\ngumgum secret delete api-token --preview\ngumgum secret delete api-token\n```\n\n## Bind resources to workers\n\nBindings project resource connection information into a worker's environment.\n\nGeneral shape:\n\n```bash\ngumgum \u003ckind\u003e bind \u003cresource-name\u003e --to \u003cworker\u003e --as \u003cENV_NAME\u003e\ngumgum \u003ckind\u003e unbind \u003cresource-name\u003e --to \u003cworker\u003e --as \u003cENV_NAME\u003e --preview\ngumgum \u003ckind\u003e unbind \u003cresource-name\u003e --to \u003cworker\u003e --as \u003cENV_NAME\u003e\n```\n\nExamples:\n\n```bash\ngumgum db bind app-db --to api --as DATABASE_URL\ngumgum kv bind app-cache --to api --as CACHE\ngumgum bucket bind uploads --to api --as UPLOADS_BUCKET\ngumgum queue bind jobs --to api --as JOBS_QUEUE\ngumgum secret bind api-token --to api --as API_TOKEN\n```\n\nBindings default to read-write access. You can request another access mode:\n\n```bash\ngumgum bucket bind uploads --to api --as UPLOADS_BUCKET --access read-only\n```\n\n## Observability and platform services\n\nGumGum boots a singleton platform stack on each server, separate from per-environment app data providers:\n\n| Service | Container | Purpose |\n| --- | --- | --- |\n| Vaultwarden | `gumgum-vaultwarden` | platform-scoped secret service |\n| OpenTelemetry Collector | `gumgum-otel` | OTLP ingress and forwarding to Tempo |\n| Prometheus | `gumgum-prometheus` | app/platform/host/container metrics |\n| Grafana | `gumgum-grafana` | dashboards, logs, traces, and datasources at `grafana.\u003cserver-domain\u003e` |\n| Loki | `gumgum-loki` | log backend |\n| Tempo | `gumgum-tempo` | trace backend |\n| Grafana Alloy | `gumgum-alloy` | Docker log shipping to Loki |\n| node-exporter | `gumgum-node-exporter` | host metrics |\n| cAdvisor | `gumgum-cadvisor` | Docker/container metrics |\n| Docker socket proxy | `gumgum-docker-proxy` | read-only Docker discovery for Prometheus |\n| Caddy | `gumgum-caddy` | TLS ingress, route labels, and ingress trace spans |\n\nDeclare app metrics, required secrets, Grafana datasource files, and dashboard files in a worker manifest:\n\n```toml\n[observability]\nenable = true\nprometheus_metrics = \"/_/metrics\"\n\n[observability.grafana]\nsources = \"../grafana/sources.json\"\n\n[[secret]]\nname = \"my-app/api-token\"\nbinding = \"API_TOKEN\"\n\n[[dashboard]]\nname = \"API Overview\"\npath = \"../grafana/dashboards/api-overview.json\"\n```\n\nWhen deployed, GumGum:\n\n- injects declared secret bindings and OTEL environment into workers\n- configures Prometheus scrapes for declared metrics endpoints\n- labels app containers for Docker service discovery (`prometheus.scrape=true`, `prometheus.port`, `prometheus.path`, `prometheus.label_*`)\n- ships GumGum-managed Docker logs to Loki with `project`, `domain`, `environment`, `worker`, and platform labels\n- enables Tempo traces from the Caddy ingress span through instrumented workers and logical provider operations\n- applies Grafana datasources and dashboards through the daemon\n- stores Grafana dashboards under `\u003ctop-level-domain\u003e` → `\u003cproject\u003e` folders\n\nGrafana dashboards should include a top-level `GUMGUM_ENV` variable for switching between `preview` and `prod`. Query app metrics with that environment label, for example:\n\n```promql\nvisit_counter_requests_total{environment=\"$GUMGUM_ENV\"}\n```\n\n`gumgum status` includes platform provider health. A healthy server with the current default stack reports `Providers: 10/10 running`.\n\nStateful platform services use host volume paths under `/gumgum/volumes/platform/*` for durability across container and machine restarts: Grafana, Prometheus, Loki, Tempo, Alloy, Vaultwarden, and Caddy all keep their data outside the container filesystem.\n\n## Deploy\n\nDeploy the current project:\n\n```bash\ngumgum deploy\n```\n\nDeploy a specific manifest:\n\n```bash\ngumgum deploy api/gumgum.toml\n```\n\nTarget a specific server:\n\n```bash\ngumgum deploy --host prod\n```\n\nDeploy the prod environment:\n\n```bash\ngumgum deploy --env prod\n```\n\nDeployments are rolling by default. GumGum starts a new revision-specific container, waits for its health check to pass, lets Caddy discover the new route labels, and then removes stale containers for that worker/route. Running containers therefore use stable GumGum labels but revision-suffixed Docker names, for example:\n\n```text\ngumgum-prod-dev-leostera-visit-counter-api-d6fcec95161ecc48\n```\n\nDelete desired deployment state for a worker:\n\n```bash\ngumgum deploy api/gumgum.toml --delete\n```\n\nPreview deploy behavior without mutating state:\n\n```bash\ngumgum --dry-run deploy\n```\n\nIngress routes are declared in worker manifests:\n\n```toml\n[[ingress]]\nname = \"http\"\nprotocol = \"http\"\nport = 3000\npublic = true\n```\n\nDefault deploy routes use the server/control-plane domain:\n\n```text\n\u003cworker\u003e.\u003cproject\u003e.\u003cserver-domain\u003e\n# api.visit-counter.leostera.dev\n```\n\n`deploy --env prod` uses the project domain from the workspace manifest:\n\n```toml\n[project]\nname = \"visit-counter\"\ndomain = \"kava.fund\"\nserver = \"starbase2\"\n```\n\nIngress records are relative to that route base:\n\n| `record` | Preview env route | Prod env route |\n| --- | --- | --- |\n| omitted | `\u003cworker\u003e.\u003cproject\u003e.\u003cserver-domain\u003e` | `\u003cworker\u003e.\u003cproject-domain\u003e` |\n| `\"api\"` | `api.\u003cproject\u003e.\u003cserver-domain\u003e` | `api.\u003cproject-domain\u003e` |\n| `\"@\"` | `\u003cproject\u003e.\u003cserver-domain\u003e` | `\u003cproject-domain\u003e` |\n\nFor example, to publish the project apex instead of `api.\u003cproject-domain\u003e`:\n\n```toml\n[[ingress]]\nname = \"http\"\nprotocol = \"http\"\nport = 3000\npublic = true\nrecord = \"@\"\n```\n\n## Logs, environment, status, and events\n\nCheck overall server status:\n\n```bash\ngumgum status\ngumgum status --host prod\n```\n\nShow logs for the current project or workspace:\n\n```bash\ngumgum logs\ngumgum logs --tail 200\n```\n\nThe CLI can still read Docker logs directly, and the platform also ships GumGum-managed Docker logs into Loki. In Grafana Explore, useful Loki selectors include:\n\n```logql\n{gumgum_managed=\"deployment\", project=\"visit-counter\"}\n{environment=\"prod\", worker=\"api-prod\"}\n```\n\nShow logs for one worker:\n\n```bash\ngumgum logs api\ngumgum logs api --tail 100\n```\n\nFollow logs for a single worker:\n\n```bash\ngumgum logs api --follow\n```\n\nPrint environment for the current project or workspace:\n\n```bash\ngumgum env\n```\n\nNarrow environment output:\n\n```bash\ngumgum env --project my-app\ngumgum env --worker api\ngumgum env --project my-app --worker api\n```\n\nEnvironment keys are namespaced by worker by default:\n\n```dotenv\nAPI_DATABASE_URL=...\nAPI_CACHE_URL=...\n```\n\nUse `--qualified` when exporting a whole workspace and you want project + worker prefixes:\n\n```bash\ngumgum env --qualified\n```\n\n```dotenv\nMY_APP_API_DATABASE_URL=...\nMY_APP_API_CACHE_URL=...\n```\n\nTrace data is sent to Tempo through the OpenTelemetry Collector. Instrumented apps can use standard OTEL environment variables projected by GumGum; Caddy emits the ingress span and propagates W3C trace context upstream. In Grafana Traces, a visit-counter request should show a chain like:\n\n```text\ngumgum-caddy api-prod-ingress\napi-prod GET /\napi-prod redis increment user counter\napi-prod minio put visit request\napi-prod redpanda produce visit event\nvisit-counter-worker-prod visit-counter.process_message\nvisit-counter-worker-prod minio get visit request\nvisit-counter-worker-prod postgres insert visit\n```\n\nShow control-plane events:\n\n```bash\ngumgum events\ngumgum events --limit 20\ngumgum events --grouped\ngumgum events --kind mutation\ngumgum events --kind reconciliation\n```\n\n## Graph inspection\n\nRender the desired graph:\n\n```bash\ngumgum graph\ngumgum graph show\n```\n\nShow the subgraph affected by a target:\n\n```bash\ngumgum graph affected worker/api\ngumgum graph affected api\n```\n\nThe target must exist in the workspace/desired graph. Invalid targets fail instead of returning an empty affected list.\n\n## Bucket object operations\n\nBucket object commands only exist under `gumgum bucket`.\n\nList objects in a bucket:\n\n```bash\ngumgum bucket ls uploads\ngumgum bucket ls uploads raw/\n```\n\nRead an object to stdout:\n\n```bash\ngumgum bucket get uploads path/to/file.json\n```\n\nCopy local to remote:\n\n```bash\ngumgum bucket cp ./local.json uploads/local.json\n```\n\nCopy remote to local:\n\n```bash\ngumgum bucket cp uploads/local.json ./local-copy.json\n```\n\nCopy remote to remote:\n\n```bash\ngumgum bucket cp uploads/local.json uploads/archive/local.json\n```\n\nSync a remote prefix to another remote prefix:\n\n```bash\ngumgum bucket sync uploads/raw uploads/archive/raw\n```\n\nRemove an object:\n\n```bash\ngumgum bucket rm uploads archive/local.json\n```\n\n## Publish\n\nPreview public publishing before changing public route state:\n\n```bash\ngumgum --dry-run publish\n```\n\nPreview a specific target:\n\n```bash\ngumgum --dry-run publish api/gumgum.toml\n```\n\nPlan an explicit public domain:\n\n```bash\ngumgum --dry-run publish api/gumgum.toml --public-domain api.example.com\n```\n\nApply publishing only after reviewing the dry-run:\n\n```bash\ngumgum publish api/gumgum.toml --public-domain api.example.com\n```\n\nWorkspace dry-runs plan each member. `--public-domain` is only valid when publishing one worker.\n\n## Rollback and revisions\n\nPreview rollback for the current project:\n\n```bash\ngumgum rollback --preview\n```\n\nPreview rollback for a worker:\n\n```bash\ngumgum rollback api/gumgum.toml --worker api --preview\n```\n\nList deployment revisions:\n\n```bash\ngumgum rollback api/gumgum.toml --worker api --revisions\ngumgum rollback api/gumgum.toml --worker api --revisions --limit 20\n```\n\nPreview or apply a specific revision:\n\n```bash\ngumgum rollback api/gumgum.toml --worker api --revision-id 12 --preview\ngumgum rollback api/gumgum.toml --worker api --revision-id 12\n```\n\nDelete stale revision metadata without changing containers:\n\n```bash\ngumgum rollback api/gumgum.toml --worker api --delete-revision-id 12\n```\n\n## Configuration\n\nView local gumgum config:\n\n```bash\ngumgum config list\n```\n\nRead and write known config keys:\n\n```bash\ngumgum config get ui.color\ngumgum config set ui.color true\ngumgum config get format\ngumgum config set format human\ngumgum config get registry_port\ngumgum config set registry_port 5000\n```\n\nConfig is schema-backed, not arbitrary key/value storage. Unknown keys fail with a human-readable error.\n\nServer-scoped config uses the same schema:\n\n```bash\ngumgum server config --host prod list\ngumgum server config --host prod get ui.color\ngumgum server config --host prod set ui.color true\n```\n\n## JSON output\n\nUse `--json` when scripting:\n\n```bash\ngumgum --json status\ngumgum --json events --limit 10\ngumgum --json db list\ngumgum --json bucket list\n```\n\nErrors are human-readable by default and structured as JSON only with `--json`.\n\n## End-to-end example\n\nThe `examples/visit-counter` workspace demonstrates:\n\n- API and background worker projects\n- preview/prod environment isolation\n- DB/KV/bucket/queue resources\n- platform-scoped secrets and observability declarations\n- Prometheus metrics from both API and worker\n- Loki logs labelled by project/domain/environment/worker\n- Tempo traces spanning Caddy → API → Redis/MinIO/Redpanda logical operations → worker → MinIO/Postgres logical operations\n- Grafana datasource/dashboard application under `\u003cdomain\u003e` → `\u003cproject\u003e` folders\n- resource bindings\n- rolling deploys with revision-suffixed containers\n- logs and environment inspection\n- events and grouped events\n- rollback previews\n- publish dry-runs\n\nTry it after configuring a server:\n\n```bash\ncd examples/visit-counter\ngumgum db create visits\ngumgum kv create user-counters\ngumgum bucket create visit-requests\ngumgum queue create visit-events\n\ngumgum db bind visits --to worker --as DATABASE_URL\ngumgum kv bind user-counters --to api --as USER_COUNTERS\ngumgum bucket bind visit-requests --to api --as VISIT_REQUESTS_BUCKET\ngumgum bucket bind visit-requests --to worker --as VISIT_REQUESTS_BUCKET\ngumgum queue bind visit-events --to api --as VISIT_EVENTS_QUEUE\ngumgum queue bind visit-events --to worker --as VISIT_EVENTS_QUEUE\n\ngumgum deploy --env preview\ngumgum deploy --env prod\ngumgum logs\ngumgum events --grouped\ngumgum --dry-run publish\n```\n\nThe API worker uses `record = \"@\"`, so prod serves the project domain itself, for example `https://kava.fund/`, while preview serves the project route under the server domain, for example `https://visit-counter.leostera.dev/`.\n\n## Command reference\n\nGlobal flags:\n\n```bash\ngumgum --json \u003ccommand\u003e\ngumgum --dry-run \u003ccommand\u003e\n```\n\nServer commands:\n\n```bash\ngumgum server\ngumgum server list\ngumgum server add \u003chost\u003e [--name \u003cname\u003e] [--user \u003cuser\u003e] [--domain \u003cdomain\u003e] [--ingress direct|cloudflare]\ngumgum server rm \u003chost-or-name\u003e\ngumgum server ping --host \u003chost-or-name\u003e\ngumgum server capabilities list --host \u003chost-or-name\u003e [--require \u003ccapability,...\u003e]\ngumgum server config --host \u003chost-or-name\u003e list|get|set\ngumgum server upgrade --host \u003chost-or-name\u003e\n```\n\nDomain commands:\n\n```bash\ngumgum domain add \u003cdomain\u003e [--server \u003cserver\u003e] [--provider manual|cloudflare] [--ingress direct|cloudflare]\n```\n\nProject/runtime commands:\n\n```bash\ngumgum init [--name \u003cname\u003e] [--domain \u003cdomain\u003e] [--server \u003cserver\u003e] [--force]\ngumgum worker create \u003cname\u003e [--port \u003cport\u003e] [--dir \u003cpath\u003e] [--force]\ngumgum worker list [workspace]\ngumgum worker delete \u003cname\u003e [workspace]\ngumgum deploy [path] [--host \u003cserver\u003e] [--env preview|prod] [--delete]\ngumgum publish [target] [--host \u003cserver\u003e] [--public-domain \u003cdomain\u003e] [--tunnel \u003ckind\u003e]\ngumgum logs [path-or-worker] [--host \u003cserver\u003e] [--tail \u003cn\u003e] [--follow]\ngumgum env [path] [--host \u003cserver\u003e] [--project \u003cname\u003e] [--worker \u003cname\u003e] [--qualified]\ngumgum status [--host \u003cserver\u003e]\ngumgum events [--host \u003cserver\u003e] [--limit \u003cn\u003e] [--kind mutation|reconciliation] [--grouped]\ngumgum graph [--host \u003cserver\u003e] [show|affected \u003ctarget\u003e]\ngumgum rollback [path] [--host \u003cserver\u003e] [--worker \u003cname\u003e] [--preview] [--revisions] [--revision-id \u003cid\u003e] [--delete-revision-id \u003cid\u003e]\n```\n\nResource commands:\n\n```bash\ngumgum db list|create|delete|bind|unbind\ngumgum kv list|create|delete|bind|unbind\ngumgum bucket list|create|delete|bind|unbind|ls|get|rm|cp|sync\ngumgum queue list|create|delete|bind|unbind\ngumgum secret list|create|delete|bind|unbind\n```\n\n## License\n\nGumGum is licensed under the GNU Affero General Public License v3.0 only (`AGPL-3.0-only`). See [`LICENSE`](LICENSE) for the full license text.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleostera%2Fgumgum","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleostera%2Fgumgum","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleostera%2Fgumgum/lists"}