> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getasym.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# GitHub template

> A real GitHub REST API backend — repos, issues, pull requests, comments, labels, tokens, and rate limiting.

The GitHub template (`apps/github-clone`, name `github`) is an API-only clone of
the [GitHub REST API](https://docs.github.com/rest): a NestJS backend on Postgres
that reproduces GitHub's core repo-collaboration surface — users/orgs,
repositories, issues, pull requests, comments, labels, milestones, branches, and
commits — using real GitHub conventions: **bare** resource paths
(`/repos/{owner}/{repo}/…`, `/rate_limit`), bare-JSON bodies (no wrapper),
`page`+`per_page` pagination via an RFC-5988 `Link` header, integer `id` +
`node_id` on every object, a `{message, documentation_url}` error envelope, and a
GitHub-style **rate limiter**. It's faithful enough at the **URL + wire-format
level** that an agent built for GitHub can operate it.

```bash theme={null}
asymmetric spin github
```

## Routing — read this first

Real GitHub serves its REST routes at the **root** (`/repos/...`, `/user`,
`/rate_limit`), with no `/api` prefix. So the backend uses **no global prefix**:
data-plane controllers bind bare GitHub paths, while the clone's own
control-plane routes keep an explicit `/api` segment so they never collide with
GitHub's namespace.

| Path                                                      | Surface                                 |
| --------------------------------------------------------- | --------------------------------------- |
| `/user`, `/users/…`, `/orgs/…`, `/repos/…`, `/rate_limit` | GitHub data plane (bare, faithful)      |
| `/api/health`                                             | infra health probe                      |
| `/api/auth/signup` · `login` · `refresh`                  | JWT web-auth for the clone              |
| `/api/user/tokens` (+ `POST`, `DELETE /:id`)              | `ghp_` personal-access-token management |

## What's inside

| Piece                  | Notes                                                                                                                                                     |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `backend/`             | NestJS REST API, JWT (access + refresh) + GitHub-style `ghp_` personal access tokens, Postgres via `pg`. Health at `/api/health`, Swagger at `/api/docs`. |
| `supabase/migrations/` | `001_initial_schema.sql` — the schema (13 tables), run on spin and replayed on reset.                                                                     |
| `seeds/`               | `acme.sql` — the bundled deterministic fixture (`acme` org + `acme/hello-world`).                                                                         |

## Modes

| Mode            | Services  | Use it for                        |
| --------------- | --------- | --------------------------------- |
| `api` (default) | `backend` | Agents that talk to the REST API. |

This template is **API-only** — there is no frontend.

## Seeding

```bash theme={null}
asymmetric spin github --seed acme           # deterministic fixture on create
asymmetric seed github-a1b2 --ai             # realistic data via the REST API
```

AI seeding generates `users`, `repositories`, and `issues` (the template's
declared entities) through real signup/repo/issue calls. See
[Seeding](/concepts/seeding).

## Tokens and auth

Every data route is guarded by default (a global `AuthGuard`); `@Public()` opts
out (`/api/auth/*`, `/api/health`). The clone authenticates two ways, both as
`Authorization: Bearer …` (the `ghp_` path also accepts `token ghp_…`):

* a **JWT** access token from `POST /api/auth/signup` / `login` / `refresh` (the
  web path), and
* a GitHub-shaped **personal access token** (`ghp_…`) minted at
  `POST /api/user/tokens`. Only `sha256(token)` is stored; the plaintext is shown
  **once** at creation.

```bash theme={null}
B=http://127.0.0.1:3006
TOK=$(curl -s $B/api/auth/signup -H 'content-type: application/json' \
  -d '{"login":"you","email":"you@acme.test","password":"password123"}' | jq -r .accessToken)
curl -s $B/user -H "authorization: Bearer $TOK"
```

Seeded users share the password `password123` (e.g. `octocat@acme.test`).

<Note>
  A `ghp_` token authenticates but its **scopes are not enforced** — any valid
  token can call anything. Scope enforcement, GitHub Apps / OAuth installs, and
  fine-grained tokens are out of scope for this slice. Reads currently require a
  token (real GitHub serves public `GET`s anonymously). Clones bind to localhost by
  default, so a token is not reachable off your machine unless you `--expose`.
</Note>

## API tour

```bash theme={null}
B=http://127.0.0.1:3006
H="authorization: Bearer $TOK"
# Read the seeded org + repo (bare GitHub paths)
curl -s $B/orgs/acme -H "$H"
curl -s $B/repos/acme/hello-world -H "$H"
# List issues (open by default) with GitHub-style pagination headers
curl -si "$B/repos/acme/hello-world/issues?per_page=2" -H "$H" | grep -i '^link:'
# Open an issue (allocates the next per-repo number, shared with PRs)
curl -s -X POST $B/repos/acme/hello-world/issues -H "$H" \
  -H 'content-type: application/json' \
  -d '{"title":"New bug","labels":["bug"]}' | jq '{number,id,node_id,title}'
# Merge a pull request
curl -s -X PUT $B/repos/acme/hello-world/pulls/4/merge -H "$H"
# Check your rate-limit budget
curl -s $B/rate_limit -H "$H" | jq '.rate'
```

### Pagination

List endpoints take `page` (1-based, default 1) and `per_page` (default 30, max
100\), return a **plain JSON array** (no wrapper), and set an RFC-5988 `Link`
header (`rel=next/prev/first/last`), CORS-exposed. Like GitHub, there is **no**
`X-Total-Count` header — the `Link` header is the only pagination signal.

### Rate limiting

A global rate limiter mirrors GitHub's: **5,000 requests/hr authenticated**,
**60/hr unauthenticated** (per-IP), on a fixed 1-hour window. Every response
carries `x-ratelimit-limit` / `-remaining` / `-used` / `-reset` / `-resource`
plus `x-github-api-version` and `x-github-media-type`. Exhaustion returns **403**
with a `retry-after` header. `GET /rate_limit` reports the live budget as
`{ resources: { core, search, graphql, … }, rate }`.

## API shape — read this before pointing an agent at it

Because GitHub is a REST API, the clone matches it at the **URL and wire-format
level** for the implemented slice, not just the capability level:

* **bare** resource paths (`/repos/{owner}/{repo}/…`, `/rate_limit`) — no `/api`
  prefix on the data plane,
* addressing by `login`, `{owner}/{repo}` full name, per-repo integer `number`,
  integer `comment_id`, label `name`, and commit `sha` — never the internal UUID,
* a numeric `id` plus a base64 `node_id` on every resource object,
* bare-JSON bodies (no envelope) and the
  `{message, documentation_url}` error envelope; 422 validation failures carry
  `errors[]` of `{resource, field, code}`,
* `Link`-header pagination and the `x-ratelimit-*` budget headers described above.

Issues and pull requests share **one** per-repo `number` sequence, exactly like
GitHub — numbers never collide within a repo.

The template ships an `API_PARITY.md` that maps each implemented route to GitHub's
live REST docs. The implemented core spans users, orgs, repositories, issues
(incl. lock/unlock), pull requests (incl. files, commits, is-merged, merge),
issue comments, labels + issue-label assignment, milestones, branches, commits,
and rate-limit — roughly **50 endpoints**, with shape parity at **49/50** against
GitHub's live docs. Whole GitHub families outside the core collaboration surface
(Actions, Checks, Search, Releases, Gists, Webhooks/Events, Git database
internals, reviews, reactions, projects, GraphQL v4, and the official MCP server)
are unimplemented by design — this is a vertical slice, not the full API. See
`apps/github-clone/API_PARITY.md` for the per-route breakdown.

<Info>
  The [`verify`](/cli/login#verify) command will fold live fidelity scoring into
  the CLI. Until then, `API_PARITY.md` and the `/verify-api github` workflow are how
  fidelity is tracked.
</Info>

## Inspect a running clone

```bash theme={null}
asymmetric query github-a1b2 "select full_name, open_issues_count from repositories"
asymmetric db github-a1b2          # prints a psql shell command
asymmetric logs github-a1b2 -f     # stream the backend
```

The schema (users, repositories, issues, pull\_requests, issue\_comments, labels,
milestones, branches, commits, …) comes straight from `001_initial_schema.sql` —
read it to know what's queryable.
