Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 1 | # Self-hosted g1t, phase 1: the core forge on your own machine. |
| 2 | # | |
| 3 | # docker compose -f deploy/self-host/docker-compose.yml up --build | |
| 4 | # | |
| 5 | # Then open http://localhost:8787. Mail (the confirmation link at sign-up) | |
| 6 | # lands in Mailpit at http://localhost:8025. The API is at | |
| 7 | # http://localhost:8789 and the MCP server at http://localhost:8789/mcp. | |
| 8 | # | |
| 9 | # What runs: the site and every core service in one workerd (g1t), git | |
| 10 | # repositories as bare repos on a volume (gitstore), the API in a second | |
| 11 | # workerd beside it, packages' files, backups and the clone pack cache in | |
| 12 | # RustFS (S3-compatible storage), and Mailpit for mail. | |
| The agents, models and context services are deleted. Nothing calls them any more: workspace agents, the hosted model proxy at models.g1t.sh with its AI Gateway, and the context hub with its search index are gone with the features they served. The deploy stack no longer builds or deploys them, so it no longer needs CONNECTORS_KEY, AI_GATEWAY_TOKEN, ANTHROPIC_API_KEY or WORKERS_AI_TOKEN, and a self-hosted install no longer generates CONNECTORS_KEY or stubs them out. Their databases, queues, index, gateway and domain stay in Cloudflare until they are deleted by hand after this is deployed. | 13 | # Cloud runners, deployments and billing are off. See |
| The docs folder is gone, and what it held lives where people read it: how a self-hosted g1t runs and how to deploy g1t to Cloudflare are pages on docs.g1t.sh under Run g1t yourself, and speed, rate limits and operating g1t.sh are sections of CONTRIBUTING.md; code that cited a file in docs/ now points to the page or section that covers it, or says what it means itself, and applied migrations and the runner images are left as they were. | 14 | # docs.g1t.sh/guides/self-hosting-architecture/. |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 15 | name: g1t |
| 16 | ||
| 17 | services: | |
| 18 | g1t: | |
| 19 | build: | |
| 20 | context: ../.. | |
| 21 | dockerfile: deploy/self-host/Dockerfile | |
| 22 | ports: | |
| 23 | - "${G1T_PORT:-8787}:8787" | |
| 24 | - "${API_PORT:-8789}:8789" | |
| 25 | environment: | |
| 26 | # Where people reach this installation. Links in mail, clone URLs and | |
| 27 | # the site's meta tags point here. | |
| 28 | PUBLIC_URL: ${PUBLIC_URL:-http://localhost:8787} | |
| 29 | # Where the API (REST, OAuth) is reached, and its OAuth issuer; empty | |
| 30 | # means PUBLIC_URL's host on API_PORT. MCP_URL, empty, is API_URL/mcp. | |
| 31 | API_URL: ${API_URL:-} | |
| 32 | MCP_URL: ${MCP_URL:-} | |
| Merge g1tusercontent.com: registry answers run nothing in a browser, the site's pages run only their own scripts, repository files and avatars on their own origin, raw files rate limited per address | 33 | # Where repository files and avatars are served: a host of its own |
| 34 | # that reaches this container, so they never share the site's | |
| 35 | # cookies; empty serves them under PUBLIC_URL/-/usercontent. | |
| 36 | USERCONTENT_URL: ${USERCONTENT_URL:-} | |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 37 | API_PORT: ${API_PORT:-8789} |
| 38 | GITSTORE_URL: http://gitstore:8080 | |
| 39 | GITSTORE_SECRET_FILE: /secrets/gitstore | |
| 40 | MAIL_URL: ${MAIL_URL:-http://mailpit:8025} | |
| 41 | MAIL_FROM: ${MAIL_FROM:-g1t <noreply@localhost>} | |
| 42 | # Your own GitHub App, for "Continue with GitHub" and importing from | |
| 43 | # GitHub. Leave these unset to have neither. See the self-hosting guide. | |
| 44 | GITHUB_APP_ID: ${GITHUB_APP_ID:-} | |
| Merge main into rm-agents: mirroring removal, open signups, free-tier limits, the g1t CLI and runner fixes, with the agent removal kept. The CLI's table of operations is regenerated from the API without the agent, customize and AI credit operations; no CLI command used them. | 45 | # Cloudflare Turnstile on sign-up: both keys, or neither. |
| 46 | TURNSTILE_SITE_KEY: ${TURNSTILE_SITE_KEY:-} | |
| 47 | TURNSTILE_SECRET: ${TURNSTILE_SECRET:-} | |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 48 | GITHUB_APP_SLUG: ${GITHUB_APP_SLUG:-} |
| 49 | GITHUB_APP_CLIENT_ID: ${GITHUB_APP_CLIENT_ID:-} | |
| 50 | GITHUB_APP_CLIENT_SECRET: ${GITHUB_APP_CLIENT_SECRET:-} | |
| 51 | GITHUB_APP_PRIVATE_KEY: ${GITHUB_APP_PRIVATE_KEY:-} | |
| 52 | GITHUB_APP_WEBHOOK_SECRET: ${GITHUB_APP_WEBHOOK_SECRET:-} | |
| Merge branch 'worktree-agent-a6da31375506fc440' | 53 | # How many friends each person may invite by email (20 when empty; |
| 54 | # 0 turns it off). Anyone may register with or without one. | |
| 55 | INVITES_PER_USER: ${INVITES_PER_USER:-} | |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 56 | # Packages' files (container images and the rest), in RustFS below or |
| 57 | # any S3-compatible store. S3_PUBLIC_ENDPOINT, when clients can reach | |
| 58 | # the store, sends large downloads there directly. | |
| 59 | S3_ENDPOINT: ${S3_ENDPOINT:-http://rustfs:9000} | |
| 60 | S3_BUCKET: ${S3_BUCKET:-g1t-packages} | |
| 61 | S3_REGION: ${S3_REGION:-us-east-1} | |
| 62 | S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-g1t} | |
| 63 | S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-g1t-packages-secret} | |
| 64 | S3_PUBLIC_ENDPOINT: ${S3_PUBLIC_ENDPOINT:-} | |
| 65 | # Nightly backups' bundles and manifests, in a bucket of their own on | |
| The docs folder is gone, and what it held lives where people read it: how a self-hosted g1t runs and how to deploy g1t to Cloudflare are pages on docs.g1t.sh under Run g1t yourself, and speed, rate limits and operating g1t.sh are sections of CONTRIBUTING.md; code that cited a file in docs/ now points to the page or section that covers it, or says what it means itself, and applied migrations and the runner images are left as they were. | 66 | # the same store (docs.g1t.sh/guides/self-hosting-architecture/#storage). |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 67 | BACKUP_S3_BUCKET: ${BACKUP_S3_BUCKET:-g1t-backups} |
| 68 | # Packs kept for fresh clones, so the next clone of the same commit | |
| 69 | # does not rebuild one; storage-setup expires them after 7 days. | |
| 70 | PACK_S3_BUCKET: ${PACK_S3_BUCKET:-g1t-git-packs} | |
| Docs know what code they describe; a project's docs folder in Docs; Docs events; files on any S3 store | 71 | # Images and files people put in Docs pages. |
| 72 | DOCS_S3_BUCKET: ${DOCS_S3_BUCKET:-g1t-docs-files} | |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 73 | volumes: |
| 74 | - g1t-data:/data | |
| 75 | - g1t-secrets:/secrets:ro | |
| 76 | depends_on: | |
| 77 | gitstore: | |
| 78 | condition: service_healthy | |
| 79 | mailpit: | |
| 80 | condition: service_started | |
| 81 | storage-setup: | |
| 82 | condition: service_completed_successfully | |
| 83 | restart: unless-stopped | |
| 84 | ||
| 85 | # The status page, in a process of its own so it stays up when the site | |
| 86 | # does not: http://localhost:8788. It checks the site every minute. | |
| 87 | status: | |
| 88 | build: | |
| 89 | context: ../.. | |
| 90 | dockerfile: deploy/self-host/Dockerfile | |
| 91 | command: ["bash", "deploy/self-host/status.sh"] | |
| 92 | ports: | |
| 93 | - "${STATUS_PORT:-8788}:8788" | |
| 94 | environment: | |
| 95 | PUBLIC_URL: ${PUBLIC_URL:-http://localhost:8787} | |
| 96 | # How the status page reaches the site, from inside Compose. | |
| 97 | STATUS_CHECK_URL: http://g1t:8787 | |
| 98 | # A public repository, `workspace/repo`, whose branches it lists as a | |
| 99 | # clone would. Empty: git is not checked. | |
| 100 | STATUS_PROBE_REPO: ${STATUS_PROBE_REPO:-} | |
| 101 | volumes: | |
| 102 | - g1t-status:/data | |
| 103 | restart: unless-stopped | |
| 104 | ||
| 105 | gitstore: | |
| 106 | build: | |
| 107 | context: ./gitstore | |
| 108 | environment: | |
| 109 | GITSTORE_URL: http://gitstore:8080 | |
| 110 | GITSTORE_SECRET_FILE: /secrets/gitstore | |
| 111 | volumes: | |
| 112 | - g1t-git:/data/git | |
| 113 | - g1t-secrets:/secrets | |
| 114 | # Not published: only the g1t container reaches it. | |
| 115 | restart: unless-stopped | |
| 116 | ||
| 117 | # Packages' files, backups and clone packs, in RustFS (S3-compatible). | |
| 118 | # Not published: only the g1t container reaches it, unless you publish | |
| 119 | # 9000 and set S3_PUBLIC_ENDPOINT. Any S3-compatible store works in its | |
| 120 | # place (S3_ENDPOINT). | |
| 121 | rustfs: | |
| 122 | image: ${RUSTFS_IMAGE:-rustfs/rustfs:1.0.1} | |
| 123 | environment: | |
| 124 | RUSTFS_ACCESS_KEY: ${S3_ACCESS_KEY_ID:-g1t} | |
| 125 | RUSTFS_SECRET_KEY: ${S3_SECRET_ACCESS_KEY:-g1t-packages-secret} | |
| 126 | RUSTFS_CONSOLE_ENABLE: "false" | |
| 127 | volumes: | |
| 128 | - g1t-objects:/data | |
| 129 | healthcheck: | |
| 130 | test: ["CMD", "curl", "-fsS", "-o", "/dev/null", "http://localhost:9000/health/ready"] | |
| 131 | interval: 5s | |
| 132 | retries: 20 | |
| 133 | restart: unless-stopped | |
| 134 | ||
| Docs know what code they describe; a project's docs folder in Docs; Docs events; files on any S3 store | 135 | # Makes the buckets once, then exits: packages' files, backups, clone |
| 136 | # packs and Docs pages' files, with a lifecycle rule on the packs' bucket that deletes | |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 137 | # packs 7 days old and uploads left unfinished after a day. |
| 138 | storage-setup: | |
| 139 | image: ${AWS_CLI_IMAGE:-amazon/aws-cli:2.37.10} | |
| 140 | depends_on: | |
| 141 | rustfs: | |
| 142 | condition: service_healthy | |
| 143 | entrypoint: | |
| 144 | - sh | |
| 145 | - -c | |
| 146 | - >- | |
| 147 | set -e; | |
| Docs know what code they describe; a project's docs folder in Docs; Docs events; files on any S3 store | 148 | for bucket in "$$S3_BUCKET" "$$BACKUP_S3_BUCKET" "$$PACK_S3_BUCKET" "$$DOCS_S3_BUCKET"; do |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 149 | aws s3api head-bucket --bucket "$$bucket" >/dev/null 2>&1 || aws s3api create-bucket --bucket "$$bucket" >/dev/null; |
| 150 | done; | |
| 151 | aws s3api put-bucket-lifecycle-configuration --bucket "$$PACK_S3_BUCKET" --lifecycle-configuration | |
| 152 | '{"Rules":[{"ID":"expire-packs","Status":"Enabled","Filter":{"Prefix":"packs/"},"Expiration":{"Days":7}},{"ID":"abort-unfinished-uploads","Status":"Enabled","Filter":{"Prefix":""},"AbortIncompleteMultipartUpload":{"DaysAfterInitiation":1}}]}'; | |
| 153 | echo "buckets ready" | |
| 154 | environment: | |
| 155 | AWS_ENDPOINT_URL: http://rustfs:9000 | |
| 156 | AWS_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:-g1t} | |
| 157 | AWS_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:-g1t-packages-secret} | |
| 158 | AWS_DEFAULT_REGION: ${S3_REGION:-us-east-1} | |
| 159 | S3_BUCKET: ${S3_BUCKET:-g1t-packages} | |
| 160 | BACKUP_S3_BUCKET: ${BACKUP_S3_BUCKET:-g1t-backups} | |
| 161 | PACK_S3_BUCKET: ${PACK_S3_BUCKET:-g1t-git-packs} | |
| Docs know what code they describe; a project's docs folder in Docs; Docs events; files on any S3 store | 162 | DOCS_S3_BUCKET: ${DOCS_S3_BUCKET:-g1t-docs-files} |
| Artifacts: an hourly view of operations and errors, and SSH must meter its git before it ships | 163 | |
| 164 | mailpit: | |
| 165 | image: axllent/mailpit:latest | |
| 166 | ports: | |
| 167 | - "${MAILPIT_PORT:-8025}:8025" | |
| 168 | # To deliver for real, relay through your SMTP server: | |
| 169 | # environment: | |
| 170 | # MP_SMTP_RELAY_HOST: smtp.example.com | |
| 171 | # MP_SMTP_RELAY_PORT: "587" | |
| 172 | # MP_SMTP_RELAY_USERNAME: ... | |
| 173 | # MP_SMTP_RELAY_PASSWORD: ... | |
| 174 | # MP_SMTP_RELAY_ALL: "true" | |
| 175 | restart: unless-stopped | |
| 176 | ||
| 177 | volumes: | |
| 178 | g1t-data: | |
| 179 | g1t-objects: | |
| 180 | g1t-status: | |
| 181 | g1t-git: | |
| 182 | g1t-secrets: |
This file's history is long; its oldest lines are credited to the oldest commit read.