~gitpocket/gitpocket

7

docs(gists): describe gist storage, the raw origin and the upload api

commit
aaf04bc4fccc074542428fc5f752f4b3cdd565a7 browse tree
parent
9c68a68a5bb0145933c82204fa4017d4d2681a4f
author
~james <354576047+[email protected]>
signature
Signed by: ~james SHA256:KlzJ0P0gvJcnOYpaxD5yCUw52ImHttTKRjVAEnnYnKg
date
2026-11-14 12:29 UTC

2 files changed, +17 −1

INSTALL.md modified

@@ -64,7 +64,7 @@ ln -s ../sites-available/gitpocket /etc/nginx/sites-enabled/
6464 nginx -t && systemctl reload nginx
6565 ```
6666
67-In the Cloudflare zone the records for `git.example.org` and `*.git.example.org` are proxied with the SSL mode set to Full, which lets the edge hold the public certificates while the origin keeps a long lived self signed pair that never needs renewing. A Cloudflare origin certificate can replace that pair when the zone runs in Full (strict) mode instead. SSH cannot pass through the proxy, so publish an unproxied `ssh.git.example.org` record and start the server with `-ssh-host ssh.git.example.org` to show that name in clone URLs while accepting that this one record reveals the address.
67+In the Cloudflare zone the records for `git.example.org` and `*.git.example.org` are proxied with the SSL mode set to Full, which lets the edge hold the public certificates while the origin keeps a long lived self signed pair that never needs renewing. A Cloudflare origin certificate can replace that pair when the zone runs in Full (strict) mode instead. The wildcard also covers `gist.git.example.org`, the cookie-less origin for raw gist files, and since `gist` is a reserved name it can never belong to a user site. SSH cannot pass through the proxy, so publish an unproxied `ssh.git.example.org` record and start the server with `-ssh-host ssh.git.example.org` to show that name in clone URLs while accepting that this one record reveals the address.
6868
6969 Custom domains reach the origin through Cloudflare for SaaS, where each verified domain becomes a custom hostname whose certificate Cloudflare issues once the owner points a CNAME at their subdomain. The script below runs every five minutes, writes the verified domains into `/etc/nginx/conf.d/gitpocket-domains.conf` and keeps the custom hostnames of the zone in step with them, reading `CF_TOKEN` and `CF_ZONE` from `/etc/gitpocket/cloudflare`, where the token needs the SSL and Certificates edit permission and `CF_SKIP` may list domains that are already proxied by a zone of your own.
7070

README.md modified

@@ -25,6 +25,7 @@ The web interface is then on `127.0.0.1:3200` and wants a reverse proxy with TLS
2525 - Pull mirrors that fetch a source on a schedule and stay read only and push mirrors that copy every branch and tag to another host after each change
2626 - Pull requests opened by pushing to `refs/for/<branch>` with no fork needed
2727 - Static sites served from `<user>.<host>` or a domain of your own, built from a `pages` repository or the `pages` branch of any repository, private ones included
28+- Gists of one or more flat files with public or secret visibility, revisions shown as diffs, stars, forks, comments, iframe embeds and a JSON API for uploads from the command line
2829 - Reports to admins and per account blocking, with hourly limits on new issues, pull requests and repositories
2930 - SSH and PGP commit verification, TOTP, access tokens and Atom feeds for commits, issues and pull requests
3031 - Site search over repositories and people with language facets
@@ -64,6 +65,21 @@ A site can also answer on a domain of your own once you claim it under settings,
6465
6566 The pages origin never sets or reads cookies and the main site only uses `__Host-` cookies, so a script running on a user site can neither read a session from the main host nor plant a cookie that the main host would later accept.
6667
68+### Gists
69+
70+A gist is a bare repository at `~you/.gists/<id>.git` holding a single `main` branch of flat files, so it counts towards your quota, moves with a renamed account and clones or pushes like any repository through `https://git.example.org/gist/<id>.git` or `[email protected]:gist/<id>.git`. The pre-receive hook refuses pushes from anyone but the owner, other branches, deletions of `main`, directories, symlinks, more than 300 files and files over 10 MB, while every save in the web editor becomes a commit whose diff appears on the revisions page.
71+
72+Secret gists are unlisted rather than private, which means anyone holding the link can read, clone or fork them while neither the gist index nor a profile ever lists one. Creating a gist needs an account at least a day old and shares the hourly upload limit, and anyone signed in may report a gist to the admins, who can delete it from its page.
73+
74+Raw files and embeds are served from `gist.git.example.org`, a separate origin that never sees a cookie, answers only `GET` and sends every file as `text/plain` or an image under a sandboxed content security policy, so a hostile file cannot run script against the main site. A pinned revision such as `/raw/<id>/<commit>/<name>` is cached as immutable, while the embed page at `/embed/<id>` may be framed by any site.
75+
76+```
77+curl -u you:TOKEN --data-binary @notes.txt 'https://git.example.org/api/gists?file=notes.txt&public=1'
78+curl -u you:TOKEN -H 'Content-Type: application/json' -d '{"description":"demo","public":false,"files":{"a.ml":{"content":"let x = 1"}}}' https://git.example.org/api/gists
79+```
80+
81+The first form prints the address of the new gist and the second answers with the same JSON that `GET /api/gists/<id>` returns, in the shape of the GitHub gist API, while uploads without `public` default to secret.
82+
6783 ### Mirrors
6884
6985 Ticking *keep it as a read only mirror* on the import page turns the import into a pull mirror that fetches the source again every 1, 4, 8 or 24 hours and refuses pushes, web edits and merges in the meantime. Push mirrors are added under the repository settings with a URL, a username and a token, and they push with `--force --prune` whenever the refs change before retrying after ten minutes if a push fails. Every fetch and push checks the URL again and resolves it once to a public address that stays pinned for the whole transfer, with redirects refused and https as the only protocol allowed. Tokens are sealed with AES-GCM under `mirror.key` in the data directory so that a copy of the database on its own does not reveal any of them.