1---
2title: Repositories
3description: Create, fork, import, and manage repositories over the typed REST plane.
4---
5
6Repositories live at `/api/v1/repos`, declared as an Effect `HttpApi`.
7One schema types the server, the client, and your tests:
8
9```typescript
10import { GitApi } from "alchemy/Git";
11import * as HttpApiClient from "effect/http-api/HttpApiClient";
12
13const client = yield* HttpApiClient.make(GitApi, { baseUrl: host });
14
15const created = yield* client.repos.create({
16 payload: { owner: "acme", name: "web", public: true },
17});
18```
19
20Everything below is also plain HTTP for `curl` and `fetch`, and the
21[GitHub facade](/git/github-api) covers Octokit.
22
23## Creating
24
25```typescript
26yield* client.repos.create({
27 payload: {
28 owner: "acme",
29 name: "web",
30 description: "the marketing site",
31 defaultBranch: "main", // default
32 public: true, // default false
33 readOnly: false,
34 },
35});
36```
37
38The response carries the repository and its clone URL. A duplicate
39`owner/name` is a typed `RepoAlreadyExists`. Who may create under an
40owner is your middleware's call; the engine attaches no meaning to the
41owner name beyond uniqueness.
42
43`owner/name` is the public identity. The repository's id is stable, so
44renaming never moves data.
45
46## Visibility
47
48`public` is a flag the engine stores and reports. What it grants is
49your middleware's decision; the tutorial's middleware serves anonymous
50reads of a public repository, REST reads, raw files, the ref
51advertisement, and `upload-pack`, and answers `401` with
52`WWW-Authenticate` otherwise, so git prompts. `Git.isRead` and
53`Git.RegistryStore` are what it asks.
54
55Listings return everything the Registry holds; `public: true` in the
56query narrows them to public repositories.
57
58## Read-only
59
60`readOnly: true` rejects pushes in-band, so the client sees a clear
61error on the ref rather than a transport failure. Use it to freeze a
62migrated repository or park one you are retiring.
63
64## Updating
65
66```typescript
67yield* client.repos.update({
68 params: { owner: "acme", repo: "web" },
69 payload: { description: "archived", readOnly: true },
70});
71```
72
73`defaultBranch` must name a branch that exists. Pointing it at a
74missing ref is a typed `RefNotFound`.
75
76## Forking and importing
77
78Fork copies a repository server-side, with no round trip through a
79client:
80
81```typescript
82yield* client.repos.fork({
83 params: { owner: "acme", repo: "web" },
84 payload: { targetOwner: "dana", targetName: "web" },
85});
86```
87
88Import pulls from any public git URL. Both return at once with
89`status: "importing"`. Poll `repos.get` until it reads `"ready"`.
90
91## Deleting
92
93```typescript
94yield* client.repos.delete({ params: { owner: "acme", repo: "web" } });
95```
96
97Delete answers `204` at once and purges in the background. The name is
98released and the objects drain through a scheduled job. A second
99delete during the drain is an idempotent `204`, and reads of a
100draining repository behave as if it were gone.
101
102## Listing
103
104```typescript
105const page = yield* client.repos.list({ query: { owner: "acme" } });
106```
107
108Paginated with an opaque cursor. Listings come from the
109[registry](/git/blocks/registry), so object counts read as zero here.
110Fetch a repository directly for live numbers.