{"contents":"# http-knot\n\nA Git server powered by the [AT Protocol](https://atproto.com/) that stores repositories in S3-compatible object storage. Part of the [Tangled](https://tangled.sh) ecosystem.\n\nhttp-knot implements the Git smart HTTP protocol for clone, fetch, and push operations, using AT Protocol decentralized identifiers (DIDs) for authentication and authorization.\n\n## Features\n\n- **Git smart HTTP protocol** — clone, fetch, and push over HTTP with standard Git clients\n- **AT Protocol authentication** — JWT-based auth tied to AT Protocol identities (DIDs)\n- **S3-backed storage** — repositories stored in any S3-compatible object store (AWS S3, Tigris, MinIO, etc.)\n- **XRPC API** — read-only endpoints for browsing repos, commits, trees, diffs, and more\n- **Role-based access control** — server owners, knot members, and collaborators via Casbin policies\n- **Event streaming** — WebSocket endpoint for repository events, plus Jetstream ingestion for AT Protocol events\n- **Spindle CI** — built-in CI/CD runner using Fly.io Machines for ephemeral build VMs\n\n## How it works\n\nRepositories are addressed by DID and name (e.g., `did:plc:abc123/my-repo`). Git operations go through the smart HTTP protocol endpoints, while the XRPC API provides a structured interface for browsing repository contents.\n\nAuthentication uses AT Protocol service auth — Git clients pass a JWT as the Basic Auth password, which is validated against the AT Protocol AppView.\n\n## Configuration\n\nAll configuration is via environment variables:\n\n| Variable | Description | Default |\n|---|---|---|\n| `KNOT_SERVER_HOSTNAME` | did:web hostname (required) | — |\n| `KNOT_SERVER_OWNER` | Server owner DID (required) | — |\n| `KNOT_SERVER_LISTEN_ADDR` | HTTP listen address | `0.0.0.0:5555` |\n| `KNOT_SERVER_DB_PATH` | SQLite database path | `knotserver.db` |\n| `KNOT_REPO_MAIN_BRANCH` | Default branch for new repos | `main` |\n| `KNOT_SERVER_PLC_URL` | PLC directory URL | `https://plc.directory` |\n| `KNOT_SERVER_JETSTREAM_ENDPOINT` | Bluesky Jetstream endpoint | `wss://jetstream1.us-west.bsky.network/subscribe` |\n\n### S3 storage\n\n| Variable | Description | Fallback |\n|---|---|---|\n| `KNOT_S3_ENDPOINT` | S3 endpoint URL | `AWS_ENDPOINT_URL_S3` |\n| `KNOT_S3_BUCKET` | Bucket name | `BUCKET_NAME` |\n| `KNOT_S3_ACCESS_KEY` | Access key ID | `AWS_ACCESS_KEY_ID` |\n| `KNOT_S3_SECRET_KEY` | Secret access key | `AWS_SECRET_ACCESS_KEY` |\n| `KNOT_S3_REGION` | AWS region | `AWS_REGION` |\n| `KNOT_S3_USE_SSL` | Use SSL for S3 | `true` |\n\n## API\n\n### Git HTTP endpoints\n\n```\nGET /{did}/{name}/info/refs # ref discovery\nPOST /{did}/{name}/git-upload-pack # clone/fetch\nPOST /{did}/{name}/git-receive-pack # push (auth required)\n```\n\n### XRPC query endpoints\n\n- `sh.tangled.repo.tree` — browse file tree\n- `sh.tangled.repo.log` — commit history\n- `sh.tangled.repo.blob` — file contents\n- `sh.tangled.repo.branches` — list branches\n- `sh.tangled.repo.tags` — list tags\n- `sh.tangled.repo.diff` — view diffs\n- `sh.tangled.repo.languages` — language statistics\n- `sh.tangled.repo.getDefaultBranch` — default branch\n- `sh.tangled.owner` — server owner identity\n- `com.atproto.repo.create` — create repository (auth required)\n\n### Events\n\n```\nGET /events # WebSocket stream of repository events\n```\n\n## Deployment\n\nhttp-knot is designed to run on [Fly.io](https://fly.io) with [Tigris](https://www.tigrisdata.com/) for S3 storage. The `fly.toml` defines two processes:\n\n- **knot** — the main HTTP server (port 5555)\n- **spindle** — CI/CD runner using Fly Machines (port 6555)\n\n```sh\nfly deploy\n```\n\n## Development\n\nRequires Go 1.24+.\n\n```sh\ngo build -o http-knot ./cmd/knot\ngo build -o spindle ./cmd/spindle\n```\n\n## License\n\nSee [LICENSE](LICENSE) for details.\n","path":"README.md","ref":"main"}