markdown · 11641 bytes Raw Blame History

Changelog

All notable changes to shithub are documented here. This project follows Keep a Changelog conventions and Semantic Versioning.

Pre-1.0 versioning: minor versions may break the API. The stability contract begins at v1.0.0; until then, expect changes between minor releases.

Unreleased

Added

  • REST API contract (S50 §0). GET /api/v1/meta returns the server's version stamp and a list of feature capability strings for client-side feature detection. Every /api/v1/* response now carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, and (when PAT-authenticated) X-OAuth-Scopes. The 403 scope-reject response also carries X-Accepted-OAuth-Scopes. Operators tune the API rate-limit budgets via ratelimit.api.authed_per_hour / ratelimit.api.anon_per_hour (defaults: 5000 / 60).
  • Pagination helper internal/web/handlers/api/apipage — emits canonical RFC 8288 Link headers (first/prev/next/last) with absolute URLs rooted at the configured public base URL.
  • REST: user emails (S50 §1). GET /api/v1/user/emails lists the authenticated user's emails. Optional ?verified=true|false filter. Scope: user:read.
  • REST: user SSH keys (S50 §1). GET/POST /api/v1/user/keys and GET/DELETE /api/v1/user/keys/{id} expose CRUD for git authentication keys. Signing keys are tracked separately by a new kind column on user_ssh_keys and remain on the HTML surface for now. Scopes: user:read for GETs, user:write for mutations.
  • Capabilities: user-emails, ssh-keys added to /api/v1/meta response.
  • REST: repos core (S50 §2). GET /api/v1/user/repos, GET /api/v1/users/{username}/repos, GET /api/v1/orgs/{org}/repos, GET /api/v1/repos/{owner}/{repo}, POST /api/v1/user/repos, POST /api/v1/orgs/{org}/repos, PATCH /api/v1/repos/{owner}/{repo} (description, has_issues, has_pulls, archived, visibility), and DELETE /api/v1/repos/{owner}/{repo} (soft-delete). Visibility-aware listing: a user's /users/{u}/repos shows private rows only to that user; an org's /orgs/{o}/repos shows private rows only to members. Single-repo GETs 404 for callers who can't see the row (no existence leak).
  • Capability: repos added to /api/v1/meta.
  • REST: issues + comments + lock (S50 §3). GET /api/v1/repos/{o}/{r}/issues (with ?state= filter and Link:-header pagination), GET /api/v1/repos/{o}/{r}/issues/{number}, POST /api/v1/repos/{o}/{r}/issues, PATCH /api/v1/repos/{o}/{r}/issues/{number} (title/body author-gated, state/state_reason policy-gated), GET / POST /api/v1/repos/{o}/{r}/issues/{number}/comments, PATCH / DELETE /api/v1/repos/{o}/{r}/issues/comments/{cid}, PUT / DELETE /api/v1/repos/{o}/{r}/issues/{number}/lock.
  • REST: repo labels (S50 §3). GET / POST /api/v1/repos/{o}/{r}/labels and GET / PATCH / DELETE /api/v1/repos/{o}/{r}/labels/{name}.
  • Capabilities: issues, labels added to /api/v1/meta.
  • REST: milestones + assignees (S50 §3 follow-up). Full CRUD for /api/v1/repos/{o}/{r}/milestones (with ?state= filter on list and live open_issues/closed_issues counters on every response), plus GET /api/v1/repos/{o}/{r}/assignees (repo owner + collaborators eligible for issue assignment). Scope: repo:read on GETs, repo:write on mutations. Mutations gate on ActionIssueLabel.
  • Issue PATCH extensions. PATCH /api/v1/repos/{o}/{r}/issues/{n} now accepts labels, assignees, and milestone fields with GitHub-style full-replace semantics. Each gates on its own policy action (ActionIssueLabel / ActionIssueAssign) so a caller missing one capability gets a clean 403 rather than a partial update. Unknown label names or assignee usernames → 422; cross-repo milestone ids → 422.
  • Capabilities: milestones, assignees added to /api/v1/meta.
  • Reach: internal/web/handlers/api.resolveAPIRepo now resolves both user-owner and org-owner repos — check-runs and every later batch implicitly gain org-repo support.

Added (internal)

  • REST: pull requests core (S50 §4). GET /api/v1/repos/{o}/{r}/pulls with ?state= and ?draft= filters, GET /api/v1/repos/{o}/{r}/pulls/{number}, POST /api/v1/repos/{o}/{r}/pulls, PATCH /api/v1/repos/{o}/{r}/pulls/{number} (title/body author-gated, state via ActionPullClose, draft→ready author-only), GET /api/v1/repos/{o}/{r}/pulls/{number}/commits, GET /api/v1/repos/{o}/{r}/pulls/{number}/files, PUT /api/v1/repos/{o}/{r}/pulls/{number}/merge (honoring the repo's default merge method and the optional sha head guard). Reviews + comments + reviewers + update-branch + auto-merge land in a follow-up.
  • Capability: pulls added to /api/v1/meta.
  • REST: PR reviews + inline comments + requested reviewers (S50 §4b). GET / POST /api/v1/repos/{o}/{r}/pulls/{number}/reviews (events APPROVE / REQUEST_CHANGES / COMMENT, with pending-draft attachment on submit), GET / POST /api/v1/repos/{o}/{r}/pulls/{number}/comments (inline review comments with file_path / side / position anchoring, pending drafts, in_reply_to_id threading), and GET / POST / DELETE /api/v1/repos/{o}/{r}/pulls/{number}/requested_reviewers (by user_id or username).
  • Capability: pr-reviews added to /api/v1/meta.
  • REST: search (S50 §5). GET /api/v1/search/repositories, GET /api/v1/search/issues?type=issue|pr, and GET /api/v1/search/code over the existing FTS corpus. Canonical gh-shaped envelope { total_count, incomplete_results, items } with Link: pagination. Anonymous callers allowed (visibility filter inside the search package narrows to public). ?q= honors the existing operator vocabulary (repo:, is:, state:, author:, phrase). The search/commits and search/users endpoints, plus the sort=/order= knobs, are deferred to follow-ups.
  • Capability: search added to /api/v1/meta.
  • REST: orgs (S50 §7). GET /api/v1/user/orgs (self), GET /api/v1/users/{username}/orgs (public; shithub has no hidden membership distinction in v1), GET /api/v1/orgs/{org} (single fetch; 404 for soft-deleted), GET /api/v1/orgs/{org}/members. Scope: user:read.
  • Capability: orgs added to /api/v1/meta.
  • REST: repo webhooks (S50 §8). Full CRUD over a repo's webhook subscriptions: GET/POST /api/v1/repos/{o}/{r}/hooks, GET/PATCH/DELETE /api/v1/repos/{o}/{r}/hooks/{id}. Deliveries read-side: GET /api/v1/repos/{o}/{r}/hooks/{id}/deliveries (paginated; Link: headers) and GET /api/v1/repos/{o}/{r}/hooks/{id}/deliveries/{did} (full transcript). POST .../deliveries/{did}/redeliver re-enqueues. Scope: repo:write; role floor: settings:general. Webhook secrets are write-only — set on create, rotated via PATCH's secret field, never echoed back. Create-time SSRF gate rejects loopback / private / disallowed-port targets so misconfigurations surface synchronously instead of as silent delivery failures.
  • Capability: webhooks added to /api/v1/meta.
  • REST: branches + tags (S50 §9). Read-only ref enumeration: GET /api/v1/repos/{o}/{r}/branches (paginated; each entry carries protected reflecting the longest-prefix match against the configured branch-protection rules, plus is_default), GET /api/v1/repos/{o}/{r}/branches/{name} (slashes in branch names accepted verbatim or URL-encoded), and GET /api/v1/repos/{o}/{r}/tags (paginated). Scope: repo:read. Empty / uninitialised repos return [] rather than 404.
  • Capabilities: branches, tags added to /api/v1/meta.
  • REST: repo collaborators (S50 §10). GET /api/v1/repos/{o}/{r}/collaborators (list), GET .../collaborators/{username} (204 membership probe), GET .../collaborators/{username}/permission (permission level — "none" when not a collaborator), PUT .../collaborators/{username} (add / upgrade, body {"role": "..."} accepting both shithub names and gh-style aliases pull/push), DELETE .../collaborators/{username} (remove). Scope: repo:read on GETs, repo:write on mutations; mutations layer ActionRepoAdmin on top. Refuses (422) to enrol the repo owner.
  • Capability: collaborators added to /api/v1/meta.

Added (internal)

  • issues.Edit orchestrator wraps UpdateIssueTitleBody with markdown re-render + cross-reference re-indexing. Used by the new PATCH-issue endpoint; available for the HTML edit flow when it lands.

Changed

  • JSON error envelope on /api/v1/*. 401 and 403 responses now emit {"error": "..."} with Content-Type: application/json (previously text/plain). Existing 4xx/5xx responses from the handler bodies are unchanged.

0.1.0 — TBD (operator fills in cutover date)

The first public release of shithub. Pre-1.0: there is no backward-compatibility promise yet. Migrations are forward-only; schema may change between minor versions.

Initial public surface

  • Identity — signup, email verification, password reset, TOTP 2FA + recovery codes, SSH keys, scoped PATs, sessions with per-account epoch invalidation.
  • Repositories — create, fork, archive, transfer, soft-delete with grace, rename with redirects, visibility toggles, branch protection, default-branch swap, topics, README/license/ .gitignore templates.
  • Git — bare repos on disk; HTTPS smart-HTTP push/pull; pre/post-receive hook integration.
  • Code browsing — tree, blob (chroma syntax highlighting), raw, blame, commit history, individual commit views, branch/tag listings, compare views, file finder.
  • Issues + PRs — full CRUD; reviews; required-reviewer enforcement; status-check gates; three merge methods.
  • Social — stars, watches, forks, /explore, stargazer/ watcher lists.
  • Search — code, repo, user, issue.
  • Notifications — in-app inbox, email fan-out, one-click unsubscribe.
  • Orgs + teams — roles, invitations, one-level nesting, max-of-sources policy.
  • Webhooks — HMAC-signed delivery, exponential backoff, auto-disable, SSRF defense, redelivery UI.
  • Observability — structured logs, Prometheus metrics, optional OTel tracing, Sentry-protocol error reporting.
  • Operations — Ansible playbook, systemd units, Caddy edge, WireGuard mesh for monitoring, Postgres WAL archive + daily logical backups to Spaces, cross-region DR, restore drill.
  • Public landing page on / for anonymous viewers; signed-in viewers get a quick-link dashboard.
  • Lightweight status page at docs.<host>/status.html.
  • Cutover artifacts under deploy/cutover/.
  • Public docs site built with mdBook.
  • Operator runbooks for incidents, backups, restore, upgrade, rollback, rotate-secrets, rotate-keys, regenerate-akc, drain-workers, read-only-mode, day-one.
  • a11y tooling (pa11y + axe) and k6 load-test scenarios.
  • THIRD_PARTY_NOTICES.md with a CI-verified generator.

Known gaps at v0.1.0

  • SSH git transport (HTTPS only)
  • Actions / CI runner
  • Packages, Releases, Pages, Projects, Gists
  • GraphQL API (only a small REST surface today)
  • Activity feed UI

These are all on the post-MVP roadmap.

View source
1 # Changelog
2
3 All notable changes to shithub are documented here. This project
4 follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5 conventions and [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
7 Pre-1.0 versioning: minor versions may break the API. The
8 stability contract begins at v1.0.0; until then, expect changes
9 between minor releases.
10
11 ## [Unreleased]
12
13 ### Added
14
15 - **REST API contract (S50 §0).** `GET /api/v1/meta` returns the
16 server's version stamp and a list of feature capability strings
17 for client-side feature detection. Every `/api/v1/*` response
18 now carries `X-RateLimit-Limit`, `X-RateLimit-Remaining`,
19 `X-RateLimit-Reset`, and (when PAT-authenticated) `X-OAuth-Scopes`.
20 The 403 scope-reject response also carries
21 `X-Accepted-OAuth-Scopes`. Operators tune the API rate-limit
22 budgets via `ratelimit.api.authed_per_hour` /
23 `ratelimit.api.anon_per_hour` (defaults: 5000 / 60).
24 - **Pagination helper** `internal/web/handlers/api/apipage`
25 emits canonical RFC 8288 Link headers (`first`/`prev`/`next`/`last`)
26 with absolute URLs rooted at the configured public base URL.
27 - **REST: user emails (S50 §1).** `GET /api/v1/user/emails` lists
28 the authenticated user's emails. Optional `?verified=true|false`
29 filter. Scope: `user:read`.
30 - **REST: user SSH keys (S50 §1).** `GET/POST /api/v1/user/keys`
31 and `GET/DELETE /api/v1/user/keys/{id}` expose CRUD for git
32 authentication keys. Signing keys are tracked separately by a
33 new `kind` column on `user_ssh_keys` and remain on the HTML
34 surface for now. Scopes: `user:read` for GETs, `user:write` for
35 mutations.
36 - **Capabilities:** `user-emails`, `ssh-keys` added to
37 `/api/v1/meta` response.
38 - **REST: repos core (S50 §2).**
39 `GET /api/v1/user/repos`, `GET /api/v1/users/{username}/repos`,
40 `GET /api/v1/orgs/{org}/repos`,
41 `GET /api/v1/repos/{owner}/{repo}`,
42 `POST /api/v1/user/repos`,
43 `POST /api/v1/orgs/{org}/repos`,
44 `PATCH /api/v1/repos/{owner}/{repo}` (description, has_issues,
45 has_pulls, archived, visibility), and
46 `DELETE /api/v1/repos/{owner}/{repo}` (soft-delete).
47 Visibility-aware listing: a user's `/users/{u}/repos` shows
48 private rows only to that user; an org's `/orgs/{o}/repos`
49 shows private rows only to members. Single-repo GETs `404`
50 for callers who can't see the row (no existence leak).
51 - **Capability:** `repos` added to `/api/v1/meta`.
52 - **REST: issues + comments + lock (S50 §3).**
53 `GET /api/v1/repos/{o}/{r}/issues` (with `?state=` filter and
54 `Link:`-header pagination),
55 `GET /api/v1/repos/{o}/{r}/issues/{number}`,
56 `POST /api/v1/repos/{o}/{r}/issues`,
57 `PATCH /api/v1/repos/{o}/{r}/issues/{number}` (title/body
58 author-gated, state/state_reason policy-gated),
59 `GET / POST /api/v1/repos/{o}/{r}/issues/{number}/comments`,
60 `PATCH / DELETE /api/v1/repos/{o}/{r}/issues/comments/{cid}`,
61 `PUT / DELETE /api/v1/repos/{o}/{r}/issues/{number}/lock`.
62 - **REST: repo labels (S50 §3).**
63 `GET / POST /api/v1/repos/{o}/{r}/labels` and
64 `GET / PATCH / DELETE /api/v1/repos/{o}/{r}/labels/{name}`.
65 - **Capabilities:** `issues`, `labels` added to `/api/v1/meta`.
66 - **REST: milestones + assignees (S50 §3 follow-up).** Full CRUD
67 for `/api/v1/repos/{o}/{r}/milestones` (with `?state=` filter
68 on list and live `open_issues`/`closed_issues` counters on
69 every response), plus `GET /api/v1/repos/{o}/{r}/assignees`
70 (repo owner + collaborators eligible for issue assignment).
71 Scope: `repo:read` on GETs, `repo:write` on mutations.
72 Mutations gate on `ActionIssueLabel`.
73 - **Issue PATCH extensions.** `PATCH /api/v1/repos/{o}/{r}/issues/{n}`
74 now accepts `labels`, `assignees`, and `milestone` fields with
75 GitHub-style full-replace semantics. Each gates on its own
76 policy action (`ActionIssueLabel` / `ActionIssueAssign`) so a
77 caller missing one capability gets a clean 403 rather than a
78 partial update. Unknown label names or assignee usernames →
79 422; cross-repo milestone ids → 422.
80 - **Capabilities:** `milestones`, `assignees` added to
81 `/api/v1/meta`.
82 - **Reach:** `internal/web/handlers/api.resolveAPIRepo` now
83 resolves both user-owner and org-owner repos — check-runs and
84 every later batch implicitly gain org-repo support.
85
86 ### Added (internal)
87
88 - **REST: pull requests core (S50 §4).**
89 `GET /api/v1/repos/{o}/{r}/pulls` with `?state=` and `?draft=`
90 filters,
91 `GET /api/v1/repos/{o}/{r}/pulls/{number}`,
92 `POST /api/v1/repos/{o}/{r}/pulls`,
93 `PATCH /api/v1/repos/{o}/{r}/pulls/{number}` (title/body
94 author-gated, state via `ActionPullClose`, draft→ready
95 author-only),
96 `GET /api/v1/repos/{o}/{r}/pulls/{number}/commits`,
97 `GET /api/v1/repos/{o}/{r}/pulls/{number}/files`,
98 `PUT /api/v1/repos/{o}/{r}/pulls/{number}/merge` (honoring
99 the repo's default merge method and the optional `sha`
100 head guard). Reviews + comments + reviewers + update-branch +
101 auto-merge land in a follow-up.
102 - **Capability:** `pulls` added to `/api/v1/meta`.
103 - **REST: PR reviews + inline comments + requested reviewers (S50 §4b).**
104 `GET / POST /api/v1/repos/{o}/{r}/pulls/{number}/reviews`
105 (events `APPROVE` / `REQUEST_CHANGES` / `COMMENT`, with
106 pending-draft attachment on submit),
107 `GET / POST /api/v1/repos/{o}/{r}/pulls/{number}/comments`
108 (inline review comments with file_path / side / position
109 anchoring, `pending` drafts, `in_reply_to_id` threading), and
110 `GET / POST / DELETE /api/v1/repos/{o}/{r}/pulls/{number}/requested_reviewers`
111 (by `user_id` or `username`).
112 - **Capability:** `pr-reviews` added to `/api/v1/meta`.
113 - **REST: search (S50 §5).** `GET /api/v1/search/repositories`,
114 `GET /api/v1/search/issues?type=issue|pr`, and
115 `GET /api/v1/search/code` over the existing FTS corpus.
116 Canonical gh-shaped envelope `{ total_count,
117 incomplete_results, items }` with `Link:` pagination.
118 Anonymous callers allowed (visibility filter inside the search
119 package narrows to public). `?q=` honors the existing operator
120 vocabulary (`repo:`, `is:`, `state:`, `author:`, phrase). The
121 `search/commits` and `search/users` endpoints, plus the
122 `sort=`/`order=` knobs, are deferred to follow-ups.
123 - **Capability:** `search` added to `/api/v1/meta`.
124 - **REST: orgs (S50 §7).** `GET /api/v1/user/orgs` (self),
125 `GET /api/v1/users/{username}/orgs` (public; shithub has no
126 hidden membership distinction in v1),
127 `GET /api/v1/orgs/{org}` (single fetch; 404 for soft-deleted),
128 `GET /api/v1/orgs/{org}/members`. Scope: `user:read`.
129 - **Capability:** `orgs` added to `/api/v1/meta`.
130 - **REST: repo webhooks (S50 §8).** Full CRUD over a repo's
131 webhook subscriptions: `GET/POST /api/v1/repos/{o}/{r}/hooks`,
132 `GET/PATCH/DELETE /api/v1/repos/{o}/{r}/hooks/{id}`. Deliveries
133 read-side: `GET /api/v1/repos/{o}/{r}/hooks/{id}/deliveries`
134 (paginated; `Link:` headers) and
135 `GET /api/v1/repos/{o}/{r}/hooks/{id}/deliveries/{did}` (full
136 transcript). `POST .../deliveries/{did}/redeliver` re-enqueues.
137 Scope: `repo:write`; role floor: settings:general. Webhook
138 secrets are write-only — set on create, rotated via PATCH's
139 `secret` field, never echoed back. Create-time SSRF gate
140 rejects loopback / private / disallowed-port targets so
141 misconfigurations surface synchronously instead of as silent
142 delivery failures.
143 - **Capability:** `webhooks` added to `/api/v1/meta`.
144 - **REST: branches + tags (S50 §9).** Read-only ref enumeration:
145 `GET /api/v1/repos/{o}/{r}/branches` (paginated; each entry
146 carries `protected` reflecting the longest-prefix match against
147 the configured branch-protection rules, plus `is_default`),
148 `GET /api/v1/repos/{o}/{r}/branches/{name}` (slashes in branch
149 names accepted verbatim or URL-encoded), and
150 `GET /api/v1/repos/{o}/{r}/tags` (paginated). Scope: `repo:read`.
151 Empty / uninitialised repos return `[]` rather than `404`.
152 - **Capabilities:** `branches`, `tags` added to `/api/v1/meta`.
153 - **REST: repo collaborators (S50 §10).**
154 `GET /api/v1/repos/{o}/{r}/collaborators` (list),
155 `GET .../collaborators/{username}` (204 membership probe),
156 `GET .../collaborators/{username}/permission` (permission
157 level — `"none"` when not a collaborator),
158 `PUT .../collaborators/{username}` (add / upgrade, body
159 `{"role": "..."}` accepting both shithub names and gh-style
160 aliases `pull`/`push`),
161 `DELETE .../collaborators/{username}` (remove). Scope:
162 `repo:read` on GETs, `repo:write` on mutations; mutations
163 layer `ActionRepoAdmin` on top. Refuses (422) to enrol the
164 repo owner.
165 - **Capability:** `collaborators` added to `/api/v1/meta`.
166
167 ### Added (internal)
168
169 - `issues.Edit` orchestrator wraps `UpdateIssueTitleBody` with
170 markdown re-render + cross-reference re-indexing. Used by the
171 new PATCH-issue endpoint; available for the HTML edit flow when
172 it lands.
173
174 ### Changed
175
176 - **JSON error envelope on `/api/v1/*`.** `401` and `403`
177 responses now emit `{"error": "..."}` with
178 `Content-Type: application/json` (previously `text/plain`).
179 Existing `4xx`/`5xx` responses from the handler bodies are
180 unchanged.
181
182 ## [0.1.0] — TBD (operator fills in cutover date)
183
184 The first public release of shithub. Pre-1.0: there is no
185 backward-compatibility promise yet. Migrations are forward-only;
186 schema may change between minor versions.
187
188 ### Initial public surface
189
190 - **Identity** — signup, email verification, password reset, TOTP
191 2FA + recovery codes, SSH keys, scoped PATs, sessions with
192 per-account epoch invalidation.
193 - **Repositories** — create, fork, archive, transfer, soft-delete
194 with grace, rename with redirects, visibility toggles, branch
195 protection, default-branch swap, topics, README/license/
196 .gitignore templates.
197 - **Git** — bare repos on disk; HTTPS smart-HTTP push/pull;
198 pre/post-receive hook integration.
199 - **Code browsing** — tree, blob (chroma syntax highlighting),
200 raw, blame, commit history, individual commit views, branch/tag
201 listings, compare views, file finder.
202 - **Issues + PRs** — full CRUD; reviews; required-reviewer
203 enforcement; status-check gates; three merge methods.
204 - **Social** — stars, watches, forks, `/explore`, stargazer/
205 watcher lists.
206 - **Search** — code, repo, user, issue.
207 - **Notifications** — in-app inbox, email fan-out, one-click
208 unsubscribe.
209 - **Orgs + teams** — roles, invitations, one-level nesting,
210 max-of-sources policy.
211 - **Webhooks** — HMAC-signed delivery, exponential backoff,
212 auto-disable, SSRF defense, redelivery UI.
213 - **Observability** — structured logs, Prometheus metrics,
214 optional OTel tracing, Sentry-protocol error reporting.
215 - **Operations** — Ansible playbook, systemd units, Caddy edge,
216 WireGuard mesh for monitoring, Postgres WAL archive + daily
217 logical backups to Spaces, cross-region DR, restore drill.
218 - **Public landing page** on `/` for anonymous viewers; signed-in
219 viewers get a quick-link dashboard.
220 - **Lightweight status page** at `docs.<host>/status.html`.
221 - **Cutover artifacts** under `deploy/cutover/`.
222 - **Public docs site** built with mdBook.
223 - **Operator runbooks** for incidents, backups, restore, upgrade,
224 rollback, rotate-secrets, rotate-keys, regenerate-akc,
225 drain-workers, read-only-mode, day-one.
226 - **a11y tooling** (pa11y + axe) and **k6 load-test scenarios**.
227 - **THIRD_PARTY_NOTICES.md** with a CI-verified generator.
228
229 ### Known gaps at v0.1.0
230
231 - SSH git transport (HTTPS only)
232 - Actions / CI runner
233 - Packages, Releases, Pages, Projects, Gists
234 - GraphQL API (only a small REST surface today)
235 - Activity feed UI
236
237 These are all on the post-MVP roadmap.
238
239 [Unreleased]: https://shithub.sh/shithub/shithub/compare/v0.1.0...trunk
240 [0.1.0]: https://shithub.sh/shithub/shithub/releases/tag/v0.1.0