Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
105 linesCodeBlameRaw

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.

The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor1#!/usr/bin/env bash
2# Starts self-hosted g1t inside its container: makes the keys it needs once,
3# writes the Wrangler configs, brings every database up to date, and runs
4# every Worker in one workerd on :8787.
5set -euo pipefail
6
7cd "$(dirname "$0")"
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member8HERE="$(pwd)"
The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor9DATA="${G1T_DATA:-/data}"
10STATE="$DATA/state"
11KEYS="$DATA/keys.env"
12GENERATED="$DATA/generated"
13WRANGLER="$(cd ../.. && pwd)/node_modules/.bin/wrangler"
14mkdir -p "$STATE"
15
16# Keys that seal secrets at rest (actions, integrations, webhooks). Made on
17# first start and kept on the volume: losing them loses those secrets.
18if [ ! -f "$KEYS" ]; then
19 umask 077
20 {
21 echo "ACTIONS_KEY=$(node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))')"
22 echo "INTEGRATIONS_KEY=$(node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))')"
23 echo "WEBHOOKS_KEY=$(node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))')"
24 } > "$KEYS"
25fi
26# Identity's key seals the GitHub tokens of linked accounts; added to keys
27# made before it existed.
28if ! grep -q '^IDENTITY_KEY=' "$KEYS"; then
29 echo "IDENTITY_KEY=$(node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))')" >> "$KEYS"
30fi
Merge remote-tracking branch 'origin/claude/cool-newton-7nm8r9' into claude/cool-newton-7nm8r931# The agents service's key seals connectors' keys and OAuth tokens
32# (Customize); added to keys made before it existed.
33if ! grep -q '^CONNECTORS_KEY=' "$KEYS"; then
34 echo "CONNECTORS_KEY=$(node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))')" >> "$KEYS"
35fi
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 address36# The site's key, which signs the short-lived addresses of private
37# repositories' files.
38if ! grep -q '^USERCONTENT_KEY=' "$KEYS"; then
39 echo "USERCONTENT_KEY=$(node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))')" >> "$KEYS"
40fi
Merge GitHub pull request #8 (claude/cool-newton-7nm8r9): Docker deployments on g1t.page behind a switch that stays off until Cloudflare's container APIs are checked live, workflows a workspace manages in its repositories, tarball and zipball downloads, refs/pull/* kept to g1t alone, framework detection for g1t.page builds, and a workflow's event payload matching GitHub's.41# The repos service's key, which seals the API's tarball and zipball
42# links and what its isolates share.
43if ! grep -q '^REPOS_KEY=' "$KEYS"; then
44 echo "REPOS_KEY=$(node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))')" >> "$KEYS"
45fi
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member46# The packages service's key, which signs registry tokens.
47if ! grep -q '^PACKAGES_TOKEN_SECRET=' "$KEYS"; then
48 echo "PACKAGES_TOKEN_SECRET=$(node -e 'console.log(require("crypto").randomBytes(32).toString("hex"))')" >> "$KEYS"
49fi
The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor50set -a
51# shellcheck disable=SC1090
52. "$KEYS"
53set +a
54
55# The git store's shared secret: given, or the file the git store made.
56if [ -z "${GITSTORE_SECRET:-}" ] && [ -n "${GITSTORE_SECRET_FILE:-}" ]; then
57 for _ in $(seq 1 60); do [ -s "$GITSTORE_SECRET_FILE" ] && break; sleep 1; done
58 GITSTORE_SECRET="$(cat "$GITSTORE_SECRET_FILE")"
59 export GITSTORE_SECRET
60fi
61[ -n "${GITSTORE_SECRET:-}" ] || { echo "No GITSTORE_SECRET or GITSTORE_SECRET_FILE." >&2; exit 1; }
62
63node configs.mjs "$GENERATED"
64cd "$GENERATED"
65
66# Migrations: the same files D1 gets, applied to the SQLite files on the
67# volume. Applied ones are recorded, so this is safe on every start.
68for config in $(cat workers.txt); do
69 for database in $(node -e "for (const d of require('./$config').d1_databases ?? []) console.log(d.database_name)"); do
70 echo "Migrating $database"
71 "$WRANGLER" d1 migrations apply "$database" --local --persist-to "$STATE" -c "$config" >/dev/null
72 done
73done
74
75args=()
76while read -r config; do args+=(-c "$config"); done < workers.txt
Packages, with a container registry on g1t.sh; workspaces deleted whole and kept 30 days; Members for every member77
78# workerd fires no cron triggers: the scheduler runs the services' crons
79# (schedules.json, from configs.mjs) once a minute through Wrangler's local
80# API, which answers only here, on localhost.
81node "$HERE/scheduler.mjs" schedules.json http://127.0.0.1:8787 &
Merge branch 'worktree-agent-aaf03bdceac799c89'82
83# The API (REST, MCP, OAuth) on a port of its own, in a second workerd. Its
84# service bindings find the Workers below through Wrangler's dev registry,
85# so it waits until they are up. It holds no data; its own KV (Actions
86# artifacts, which need the runner) is kept apart from the site's. It
87# listens on 8789 in the container; API_PORT is the port people use, which
88# API_URL is derived from (configs.mjs).
89(
90 for _ in $(seq 1 120); do
91 node -e "fetch('http://127.0.0.1:8787/').then(() => process.exit(0), () => process.exit(1))" && break
92 sleep 2
93 done
94 echo "The API is starting on $(node -p "require('./api.json').api") (MCP: $(node -p "require('./api.json').mcp"))"
95 exec "$WRANGLER" dev -c g1t-api.json \
96 --ip 0.0.0.0 --port 8789 \
97 --persist-to "$DATA/api-state" \
98 --show-interactive-dev-session=false
99) &
100
The sidebar asks billing whether it is on, not for the account, so no page waits on the card processor101echo "g1t is starting on ${PUBLIC_URL:-http://localhost:8787}"
102exec "$WRANGLER" dev "${args[@]}" \
103 --ip 0.0.0.0 --port 8787 \
104 --persist-to "$STATE" \
105 --show-interactive-dev-session=false

This file's history is long; its oldest lines are credited to the oldest commit read.