Repository navigation
Releases
v0.19.0
0.19.0 (2026-10-30)
⚠ BREAKING CHANGES
- api: a diff-commit patch that does not apply now gets 409 conflict instead of 422 invalid, and a diff payload that is not a valid patch or that names an invalid path now gets 400 invalid instead of 422.
- api:
GET .../fileswithoutrecursivelists every file in the tree instead of the direct children. It answers 422 when that listing passes the 64 MiB git output budget. Sendrecursive=falseto get the direct children. - api: when another writer moves the branch after the server wrote the commit, commit-pack and diff-commit now answer 409 with result.status failed instead of conflict, and the body carries the written commit in commit and result.new_sha.
- api: GitHub sync creates during a rolling upgrade. Only an upgraded worker adopts the GitHub default branch for a sync create that omits default_branch. A sync that an older worker runs leaves the repo on main until the next sync by an upgraded worker. An older replica's PATCH /repo to main before that sync does not stop the adoption, so the repo still moves to the GitHub branch. Upgrade every replica before you send GitHub sync creates that omit default_branch, or send default_branch explicitly. Tracked in #609.
- api: a commit-pack or diff-commit request that changes nothing now gets 412 precondition_failed "no changes to commit" instead of an empty commit. A commit-pack or diff-commit stream that ends before its eof chunk now gets 412 instead of 400.
- api: GET /api/v1/repos/files/metadata and GET /api/repos/{repo_name}/files/metadata now send only the subject line of each commit message in the commits map (the first paragraph as one line, the same as git's %s). Clients that need the body or the trailers must read GET /api/v1/repos/commit.
- api: GET /api/v1/repos/commits and GET /api/repos/{repo_name}/commits now send only the subject line of each commit message (the first paragraph as one line, the same as git's %s). Clients that need the body or the trailers must read GET /api/v1/repos/commit.
- api:
filesandfiles/metadatacalls withoutlimitandcursorreturn every entry instead of one page, andfiles/metadataanswers 429 when the files underpathhave more than 50,000 distinct last commits. - api: a failed merge guard on POST /api/v1/repos/merge and POST /api/repos/{repo_name}/merge now answers code ref_guard_failed instead of conflict, with guard and current_sha members. A merge with expected_source_sha whose source branch does not exist answers 409 ref_guard_failed with current_sha 40 zeros instead of 404. Clients that branch on code conflict for a merge guard must check ref_guard_failed.
- api: a stale expected_target_sha on POST /api/v1/repos/merge and POST /api/repos/{repo_name}/merge now answers 409 with a problem body, code conflict, instead of 412 with result.status precondition_failed. Raw HTTP callers that check for 412 must check for 409 and the conflict code. SDK callers keep the same error type.
- api:
POST /reposnow answers 200 with the existing repo instead of 409 for a plain, copy or fork create of a name that a finished repo without an upstream holds. A repeat copy or fork answers before it reads its source. - api:
POST /reposnow answers 503 withRetry-Afterinstead of 409 for a plain, copy or fork create of a name whose fork or copy create has not finished, including one that crashed or whose last step failed. A sync create pending after its upstream link was removed gets the same answer.DELETE /repos/deleteclears such a name. - s3wal: S3WAL only, PostgreSQL and SQLite are not affected. An S3WAL bucket written by v0.16.0 through v0.18.0 must be converted before this release serves it. Stop
serveand wait for it to exit, take a backup, replace the binary, runopenstorage s3wal-meta convert --to 2, then startserve. Upgrade an older bucket to v0.18.0 first. To go back, stopserve, take a backup, and runopenstorage s3wal-meta convert --to 1with this release before starting the older release. See docs/how-to/deploy-production.md#upgrading-the-s3wal-metadata-format. - api:
POST /reposwithupstreamor abase_reposync, for a name that a repo with the same upstream and the sameforward_pushesholds, now answers 200 with the existing repo instead of 409. A repeat create of a pending sync repo by a token with onlyrepo:writenow answers 403 instead of 409.
Features
- api: a fork or copy is pending until its ref write (#450) (49bc1ac)
- api: a repeat create of a finished repo answers 200 (#453) (295de82)
- api: a repeat sync create of the same upstream answers 200 (#438) (0a4994f)
- api: answer a stale merge expected_target_sha with 409 conflict (#536) (198cc45)
- api: answer failed merge guards with 409 ref_guard_failed (#571) (d7c1eb8)
- api: honor expected_source_sha on merge (#535) (0341af9)
- api: name invalid fields from the single-field body validators (#443) (e1beeeb)
- api: name the invalid field in invalid_params on body decode failures (#436) (66f5c32)
- api: send a Deprecation header on the deprecated pull-upstream and base routes (#460) (ea43a01)
- api: send only the subject line in commit list entries (#575) (f0c975a)
- api: send only the subject line in files/metadata commits (#579) (5e9bbe6)
- meta: add the ADR-50 schedule transitions for S3WAL metadata v2 (#448) (27b24bd)
- meta: add the S3WAL metadata v1/v2 conversion (ADR-51) (#441) (ba8365a)
- meta: complete the S3WAL local schedule (#449) (ae6e3a0)
- s3wal: repair or refuse unfinished creates on convert --to 1 (#489) (5b06087)
- s3wal: run S3WAL upstream syncs on the durable local schedule (#451) (f0153d4)
- s3wal: schedule upstream syncs from S3WAL git-surface pushes (#454) (f5d362a)
- s3wal: serve reads metadata format v2; add s3wal-meta convert (#444) (116175f)
- s3wal: store create state in metadata v2; release-pair harness covers conversion and downgrade (#446) (cb885a7)
Bug Fixes
- api: add ref_guard_failed problem members to the notes 409s (#573) (0e6133b)
- api: answer "repository already detached" on DELETE .../upstream without an upstream (#468) (6005169)
- api: answer 409 conflict when a diff-commit patch does not apply (#611) (77424c8)
- api: answer 412 no changes to commit on commit writes that change nothing (#605) (54f9bca)
- api: answer 413 when a commit-pack passes 262144000 decoded bytes (#610) (e491cf4)
- api: answer a lost commit CAS with 409 fa...
v0.18.0
0.18.0 (2026-10-13)
⚠ BREAKING CHANGES
- sync: a REST write or git push from a token whose ref policies carry any op no longer schedules the inbound upstream sync on PostgreSQL, or for REST writes on SQLite and S3WAL. The write stays local, and the repository converges on the next write, pull-upstream or push-upstream from a token without policy ops. In a mixed fleet, replicas on an older binary still schedule the sync for such writes until they are upgraded.
Features
- api: add RFC 9457 instance to problem-details error bodies (#420) (6b23ae9)
- api: emit upstream's specific not-found and conflict problem codes (#435) (5cadc73)
- api: emit upstream's specific problem codes from the central helpers (#428) (b14fa58)
- api: pull-upstream finishes a pending sync create (#432) (deef8b1)
Bug Fixes
- deps: bump Go toolchain to 1.27.2 for stdlib security fixes (#434) (04a3eea)
- jobs: a PostgreSQL sync create completes only while its upstream is linked (#423) (c034b20)
- release: keep release binaries' VCS stamp clean (#415) (3bd4897)
- s3wal: restart a pull that crashed holding its claim (#427) (464c1c2)
- sync: deny the implicit sync poke for ref-policy-restricted tokens (#426) (dacd7cd)
v0.17.0
0.17.0 (2026-10-12)
⚠ BREAKING CHANGES
- config: openstorage no longer reads CODESTORAGE_* environment variables. Rename each one to its OPENSTORAGE_* name before you upgrade. A process that still sets an old name without its replacement refuses to start and names the variables to rename.
Features
v0.16.0
⚠ Upgrade notes: breaking and behavior changes since 0.15.0
Read this section before you upgrade. The generated changelog follows below.
Caution
If your organization uses RSA signing keys under 2048 bits, rotate them on 0.15.x before you upgrade. 0.16.0 rejects weak RSA keys at startup and on every request, so tokens signed with them stop working (item 7).
Wire and API changes
- Outbound webhooks are renamed. Outbound push-upstream runs now send
repo.push_upstream.started,repo.push_upstream.succeededandrepo.push_upstream.failedinstead ofrepo.sync.*withdirection: "push". The body is the same apart fromtype. Receivers that filteredrepo.sync.*ondirection == "push"must handle the new types. During a rolling upgrade, replicas that are not yet upgraded and deliveries queued before the upgrade keep the old names. (#358, webhooks reference) next_cursoris omitted on the last page. The repos, branches, commits, files, files/metadata, notes/refs and tags list responses sendnext_cursoronly whenhas_moreistrue. Exhausted pages no longer send"next_cursor": "". Clients must stop paging onhas_more: false, not on an empty cursor.grepis unchanged. (#377)POST /api/v1/reposreturns 201 on create. A new repository (plain, sync, copy or fork) returns201instead of200, with the messageRepository '<name>' created. A name that a deleted repository still holds returns404instead of409, and nothing is restored. Every live taken name still returns409. The Go and TypeScript SDKs accept any 2xx. (#390)Commit.dateis the author date.GET /repos/commits,GET /repos/commitand their path-style aliases now send the author date indateand the committer date in the newcommitter_datefield. Before,datecarried the committer date, so rebased and cherry-picked commits now show a differentdate. (#401)- Ref-policy-restricted tokens cannot create sync repos.
POST /reposwithupstreamor abase_reposync now returns403when the token's ref policies carry any op. The server refuses before it creates the repository, the credential or the first sync. Plain creates are unchanged. (#395)
Sync credentials
-
Per-host default git usernames and a credential helper. When a stored git credential has no username, the sync worker now sends the upstream provider's default on the pull fetch and the upstream push. A stored username always wins. (#398, #402)
- Bitbucket:
x-token-authwith the token as the password (wasgitbefore #398). - GitLab:
oauth2with the token as the password (wasgit). - Gitea, Forgejo, Codeberg: the token as the username with an empty password (was
git:<token>). - GitHub and every other provider:
gitwith the token as the password (unchanged).
The worker now gives git its credentials through a static credential helper that reads them from the environment, and it resets any other configured credential helper for these runs. A stored username or password that contains CR, LF or NUL now fails the sync as a configuration error. The error does not echo the value.
- Bitbucket:
Authentication
-
RSA organization keys must be at least 2048 bits. The server enforces this at key ingestion and on every request, including keys already stored. A key set that mixes strong and weak RSA keys fails closed at startup and at verification. CAUTION: if you use RSA keys under 2048 bits, rotate them on the old fleet before you upgrade, or every token signed with them stops working. (#253, rotation steps)
-
JWTs must use canonical, unpadded base64url. The verifier now rejects padded, standard-alphabet or otherwise non-canonical spellings in the header, payload and signature segments with
401. Tokens minted by the SDKs and byopenstorage mintalready use the canonical form. Custom token minters must emit canonical unpadded base64url. (#323)
Read-side limits and request parsing
- Read-side git work is bounded. The
git:readendpoints now answer422when a request hits the memory, output or file-size limit,503withRetry-Afterwhen capacity is busy, and503when the 30 s git time budget (archive: 10 min) runs out. Write endpoints are unchanged. (#324) - Strict JSON bodies reject duplicate member names with
400. Uppercase full SHAs, including guard SHAs, are now accepted instead of rejected. (#384, #386)
Go module path and installation
- The Go module path is now
github.com/coder/openstorage(wasgithub.com/coder/open-storage). Updatego installcommands,go.modrequirements and imports that use the old path.go install github.com/coder/openstorage/cmd/openstorage@v0.16.0now works for users with access to this repository. The repository is not public, so proxy.golang.org cannot serve it: setGOPRIVATE=github.com/coder/openstorageand give git your GitHub credentials. The release archives below need no Go toolchain, andmise use github:coder/openstorage@0.16.0installs the same binary whenGITHUB_TOKENis set. The binary name (openstorage), theOPENSTORAGE_*configuration prefix and persistent data locations are unchanged. (#225, migration guide)
Configuration
- The
CODESTORAGE_*fallback still works in 0.16.0. The docs said this fallback lasts for 0.15.x only. 0.16.0 still resolves each unsetOPENSTORAGE_*variable from itsCODESTORAGE_*name and logs a boot-time deprecation warning for each one. Do not rely on it: migrate every setting toOPENSTORAGE_*now. A later release removes the fallback; that version is not decided yet. (migration guide)
Fixes missing from the generated changelog: fork create (POST /api/v1/repos with a base_repo fork, and the copy form) no longer returns 500 when $TMPDIR and the data dir are on different filesystems (#362). The JWT change in item 8 (#323) is also missing below.
Additive changes worth knowing: error bodies gain RFC 9457 problem-details fields next to error (#351). Webhooks also send org, type and repository.repo_name (#368). An opt-in Prometheus listener ships behind OPENSTORAGE_METRICS_LISTEN, off by default (#327).
This release ships binaries only. No container image is published for 0.16.0.
0.16.0 (2026-10-09)
⚠ BREAKING CHANGES
- api: Commit.date now means the author date. Read the new committer_date field for the committer date.
- api: POST /api/v1/repos returns 201 instead of 200 for a new repository, the success message text changes, and a create of a deleted repo's name returns 404 instead of 409. The Go and TypeScript SDKs accept any 2xx.
- webhooks: send outbound push-upstream runs as repo.push_upstream.* events (#358)
Features
- api: accept optional repo_name in POST /repos (#299) (3a68bc9)
- api: accept restore-commit base_ref with the spec's 412 preconditions (#383) (b0ddce7)
- api: accept upstream on repo create and expose it on repo GET (#361) (f6a6d33)
- api: add organization metadata endpoint (#286) (536e096)
- api: add RFC 9457 problem-details fields to the error envelope (#351) (1f392ac)
- api: add topological order rank to files/metadata commits (#337) (516ee3d)
- api: allocate omitted branch targets (#239) (3663fae)
- api: answer POST /repos 201 on create and 404 for a deleted name (#390) (127be7d)
- api: bound memory, time and concurrency of read-side gi...
v0.15.0
open.storage 0.15.0
open.storage is a self-hosted Git storage backend with Git smart HTTP, Git LFS, and a REST API.
Distribution and configuration
- Repository:
github.com/coder/openstorage. - Command-line executable:
openstorage. - Configuration:
OPENSTORAGE_*environment variables. Explicitly configured values take precedence, including empty values. - Release downloads: Linux and macOS archives for amd64 and arm64, plus
checksums.txt. - Container image: build
ghcr.io/coder/openstoragelocally; registry publishing is not configured.
Installation
See the README for installation and usage, and the configuration reference for deployment settings.
Generated with mux • Model: coder:openai/gpt-6-astra • Thinking: high
v0.14.6
v0.14.5
v0.14.4
Warning
Broken release — no binary assets. The darwin asset build failed at tag time (syscall.Stat_t platform-width mismatch introduced in #197, fixed in #199). Use v0.14.5 instead, which contains identical functionality plus the fix.
0.14.4 (2026-08-28)
Bug Fixes
Generated with mux • Model: coder:openai/gpt-6-astra • Thinking: high