Version: 0.1 ยท Status: Draft

gitxt is a purely decentralised git forge. There is no federation protocol, no instance-to-instance API, no accounts on other people’s servers. Each forge instance is fully independent; collaboration is an act of publishing and reading plain text files over HTTP โ€” exactly the twtxt model, applied to code forges.

Everything below can be implemented with git, an HTTP server, and a text editor. A conforming instance interoperates with every other conforming instance and with the existing twtxt ecosystem.

1. The forge feed

Every repository publishes one forge feed: an append-only, UTF-8, plain-text file conventionally served at:

https://example.com/~user/repo/forge.txt

The feed MUST be a valid twtxt feed: one entry per line, each line an RFC3339 timestamp, a TAB (\t), and the entry text. Multi-line text is escaped with U+2028 (LINE SEPARATOR) per the twtxt Multiline Extension. Any stock twtxt client can follow, render, and reply to a forge feed; forge semantics are carried entirely by the conventions in ยง3 and degrade gracefully to readable text.

2. Feed metadata

The feed begins with twtxt Metadata Extension comments:

# nick = myproj
# url = https://example.com/~alice/myproj/forge.txt
# type = forge
# clone = https://example.com/~alice/myproj.git
# description = a tiny project
# key = ssh-ed25519 AAAAโ€ฆ
# origin = https://upstream.example/~bob/myproj/forge.txt
# ping = https://example.com/ping
field req. meaning
nick MUST the repository name
url MUST the feed’s canonical URL โ€” the identity of this repo (ยง5)
type MUST literally forge; distinguishes forge feeds from personal twtxt feeds
clone MUST anonymous HTTP(S) clone URL of the repository
description MAY one-line description
key MAY the maintainer’s SSH public key, for optional signing/verification
origin MAY for forks: the upstream repository’s feed URL
ping MAY endpoint accepting ping nudges (ยง6)

3. Entries

Entry text follows plain twtxt conventions โ€” Twt Hash threading with (#hash) subjects and @<nick url> mentions โ€” plus two forge conventions:

  1. an optional verb, a leading bracketed token after the subject;
  2. optional trailers, trailing key=value tokens.
<timestamp> TAB [(#root)] [verb] free text [key=value โ€ฆ]

Recognised verbs:

verb starts a thread? meaning
[issue] yes opens an issue; the text is its title
[patch] either proposes changes; carries repo= and ref= trailers (ยง4)
[close] no closes the thread it replies to
[reopen] no reopens the thread it replies to
[merge] no records that a patch in this thread was merged; MAY carry commit=
(none) no a plain comment in the thread

Recognised trailers: repo, ref, branch, commit. Unknown verbs and trailers MUST be ignored (they remain visible as text).

Threading is exactly the twtxt Twt Hash mechanism: a reply carries (#hash) of the thread root. Hashes are computed from the feed’s canonical url, the entry timestamp, and the entry text, per the Twt Hash spec (v1 or v2 according to the entry’s timestamp epoch).

An example conversation across two independent instances:

# bob's fork feed (https://bob.example/~bob/myproj/forge.txt)
2026-09-01T10:00:00Z	[issue] Crash on empty input
2026-09-01T12:30:00Z	(#a3f9k2c) [patch] fix crash repo=https://bob.example/~bob/myproj.git ref=fix-crash

# alice's upstream feed (https://alice.example/~alice/myproj/forge.txt)
2026-09-01T13:00:00Z	(#a3f9k2c) [merge] merged @<myproj https://bob.example/~bob/myproj/forge.txt> (#p7x1m9q) into main commit=deadbee
2026-09-01T13:01:00Z	(#a3f9k2c) [close] fixed by the patch

4. Patches

A patch is a pointer, not a payload: the code travels by git itself.

Instances SHOULD fetch patch refs only over http(s) and treat fetched objects as untrusted input.

5. Identity and trust

A feed URL is an identity. There is no global namespace, registration, or cryptographic requirement. @<nick url> mentions name people and repos; following a feed is trusting its URL.

Trust in content is human: a maintainer reviews a patch’s diff before merging, exactly as they would review an emailed patch. A feed MAY advertise an SSH public key (key =); entries and git objects MAY be signed with it and clients MAY verify โ€” but verification is a client feature, never a protocol requirement.

6. Transport: pull, plus an optional ping

The only required transport is HTTP GET. Instances follow feeds and poll them (honouring ETag / Last-Modified). A follower SHOULD send a descriptive User-Agent naming its own feed, twtxt-style, so that being fetched doubles as passive discovery:

gitxt/0.1 (+https://bob.example/; @bob)

As a timeliness optimisation โ€” never a requirement โ€” an instance MAY expose a ping endpoint (advertised via ping =) accepting:

POST /ping
url=https://bob.example/~bob/myproj/forge.txt

The receiver MAY fetch a followed feed immediately, MAY queue an unknown feed for its owner to inspect, and MUST NOT trust the pinged URL beyond fetching it. Receivers SHOULD rate-limit. If every ping in the world is lost, nothing breaks: polling still delivers everything.

7. Repositories

Repositories are served with the standard git smart-HTTP protocol at the clone URL. Anonymous read (git-upload-pack) MUST be open; write (git-receive-pack) MUST be authenticated โ€” how is an instance-local matter (gitxt uses a bearer token as the HTTP basic-auth password).

Forking is a local act: clone the upstream (found via its feed’s clone), publish your own feed for the fork with origin = naming the upstream feed, and follow that upstream feed.

8. What is deliberately absent

No instance-to-instance API. No shared databases. No global identity. No required cryptography. No JSON. A forge feed is useful with nothing but curl and git โ€” that is the point.