English | 简体中文
This document owns Ferry's release identity, how a CI candidate becomes a published version, and the acceptance record for 1.0.0. Account secrets never belong here.
- Decided (Max, 2026-09-14): the first version is
1.0.0, taggedv1.0.0. Versions are fixed and published by hand; there is no rollingedgeorlatesttag. - Decided (Max, 2026-09-14): a release publishes the Server + Web image and a deployment bundle. It does not release the native apps; iOS stays on internal TestFlight and Android is built from source.
- Image:
ghcr.io/max1874/ferry:<version>forlinux/amd64andlinux/arm64, one multi-architecture index. The image carriesorg.opencontainers.image.revisionwith the source commit. - Compose download:
ferry-<version>-docker-compose.tar.gz, the only uploaded asset: aferry/directory holdingcompose.yaml,.env.exampleandscripts/ferry-data.sh. It is configuration, not the program; the runnable program is the image. GitHub shows each asset's SHA-256, so there is no separate checksum file. - Decided (Max, 2026-09-14): the GitHub Release body is English:
docs/releases/<version>.mdwithout its title and language line, then a link to the Chinese notes and one small line with the image digest, commit and CI run. The notes open by saying which file to download. - Authentication: the repository's
GITHUB_TOKENonly. Every action is pinned to a commit SHA. - Decided (Max, 2026-09-15): CI runs only when started by hand (
workflow_dispatch), never on a push or pull request.
-
CI builds the candidate. Start CI on
mainby hand withgh workflow run ci.yml --ref main. Itsimagejob builds both architectures once, checks that the local registry and the OCI archive hold the same index digest, starts each architecture throughcompose.yaml, keeps a device, text, file and password across a restart, and moves a source-built deployment to the image without losing that data. It uploadsferry-candidate: the OCI archive, the bundle,candidate.json(commit, ref, run, digest, platforms) andSHA256SUMS. -
The version files already name the version.
compose.yamland.env.exampledefault toghcr.io/max1874/ferry:<version>, andscripts/check-repo.shkeeps the two equal. Bump both, and add the release notes, in a commit before choosing a candidate. -
Acceptance evidence is recorded in the checklist below, and Max explicitly authorizes the release.
-
Run the workflow with the version and the successful CI run on
main:gh workflow run release.yml -f version=1.0.0 -f ci_run_id=<run id>
The workflow refuses to continue unless the run is a successful
CIrun onmain(started by hand, or a push run from before 2026-09-15), its commit is still onmain, no Release or tag exists for the version, the artifact checksums and digest match the record, and the bundle names the requested image. It pushes the archived image without rebuilding, keeping its digest, and re-reads the registry digest. An existing version under a different digest is a refusal, not an overwrite; the same digest lets a re-run continue. -
First release only: make the package public. GitHub creates a new container package as private. The workflow then fails at the anonymous pull with a link to the package settings. Set the package to Public and re-run the workflow.
-
Anonymous pull. With an empty Docker client configuration, the workflow pulls both architectures by tag, checks they resolve to the released digest, and runs the container journey on each.
-
Draft release. The workflow creates a draft
v<version>Release on the candidate commit with the Compose download, English notes linking the Chinese notes, image digest and CI run. Publishing the draft is Max's step and creates the tag. -
Point the READMEs at the new version. After publishing, confirm that the image pulls anonymously and the Compose download URL answers, then change the version in both READMEs: the download commands, the image named in the quick start and the
FERRY_IMAGEdefault in Settings. Until then the READMEs keep naming the previous release, never an unpublished download. -
After publishing, update the supported-version wording in
SECURITY.mdandCONTRIBUTING.mdand their Chinese siblings.
Frozen on 2026-09-14. Status values: done with evidence, pending with an owner, or blocked.
| # | Item | Status |
|---|---|---|
| 1 | Web: empty-timeline guide and Connect another device; Devices connect section with address, copy, local QR code and private-network note; local-only address shows a hint instead of a QR code | implemented; the join and transfer journey passed in real browsers (item 9); the edge cases in item 10 were skipped |
| 2 | Startup log names the listener and the browser address separately | implemented; the CI container journey asserts the loopback message |
| 3 | compose.yaml pulls the image; compose.build.yaml builds the source; .env.example holds image and network settings |
implemented |
| 4 | CI builds, starts and checks both architectures, source-to-image upgrade and backup/restore self-test against the current source, and keeps the candidate | done (2026-09-14): release run 34818862090 accepted the successful CI push run for 84c355b as its candidate |
| 5 | Release workflow: verified run and artifact, no rebuild, no overwrite, anonymous pull, draft Release | done (2026-09-14): release run 34818862090 pushed the 84c355b candidate and created the draft Release |
| 6 | README and this document in English and Chinese; product core records the first-run journey | done |
| 7 | Isolated deployment from an empty directory following the README, restart keeping data, existing source deployment moved to the image, backup and restore | done (2026-09-14), with limits: on an arm64 macOS host that already had Docker, with no earlier Ferry container, volume or image, the README quick start ran in an empty directory from the published Compose download. Steps 1–5 took about 13 seconds, pulled the released linux/arm64 image, and the log named the LAN address to open. A browser on another computer joined directly and showed the Devices QR code; a second, isolated browser session joined, and text, an image and a file crossed between them, the downloaded file matching byte for byte. A restart, then the README backup and restore commands, kept both sessions joined with their messages. The walk-through was run by someone who already knew Ferry, so it does not show where a new user gets stuck (see After 1.0.0). Not covered: a phone (see item 9), the Copy control, the password, and moving a source deployment (CI covers that move) |
| 8 | Real desktop and phone screenshots and a ~15 second transfer GIF from the final candidate, each file under 3 MiB | deferred (Max, 2026-09-14); the README keeps the existing screenshot |
| 9 | Real computer and phone browsers: QR join, text send and copy, image preview, file download, without and with a password; record each device and browser | done (Max, 2026-09-14): on the test server deployment of 84c355b with host networking, an iPhone camera scan opened Safari and joined; text send and copy, image preview and file download passed without and with a password, with a Mac browser as the other device. Exact iPhone model, iOS version and desktop browser were not recorded. The native apps have no scan entry; scanning always joins through Safari |
| 10 | Local-only hint, IPv4, IPv6, private proxy domain, copy failure, QR failure, offline and reconnect | skipped by Max's decision (2026-09-14); unverified |
| 11 | Package Public and anonymous pull of both architectures | done (2026-09-14): the package was already public. Release run 34818862090 pulled both architectures without credentials and ran the container journey on each, and an anonymous manifest request from outside CI returned linux/amd64 and linux/arm64 under sha256:3eba51a1d91902543afd91c8f2c3eed85deb450d291a20d6b5599402e41a5bce, the CI candidate's digest |
| 12 | Max authorizes and publishes the draft Release | done (Max, 2026-09-14T11:27Z): v1.0.0 points to 84c355b. After publishing, the asset was renamed ferry-1.0.0-docker-compose.tar.gz, the checksum file removed and the body switched to English; the new URL answers anonymously and the old one no longer exists |
| 13 | README quick start switches from the source build to the bundle after the image pulls anonymously and the bundle URL answers; until then the README must not point at unpublished downloads | done (2026-09-14) |
Browser acceptance is not native-app acceptance, and CI container runs are not real-device evidence.
- Observed (2026-09-14): 1.0.0 was built from
84c355b, before later commits onmain. Its Web app still carries the icon that360c300redrew, and thecompose.yamlin its Compose download still setsTZ: Asia/Shanghai, a maintainer-local default thatmainhas since removed; the Alpine image has no time-zone data, so that setting has no reliable effect. Both changes reach users only in the next release. They shipped in 1.0.1.
- Observed (2026-09-15):
v1.0.1points toc7f10d7, released by run 34922661122 from CI run 34922312922 undersha256:a3dc11c83cc993d5408a2ed528a9f5df9c02bb0766be443d009c14b8ea5e84d5. Outside CI, an anonymous manifest request returnedlinux/amd64andlinux/arm64, and the Compose download URL answered anonymously. It was the first release from a manually started CI run. - Observed (2026-09-15): the first CI run for 1.0.1 failed before the Android build.
setup-androidinstallstools platform-toolsby default, andsdkmanagernow fails withFailed to find package 'tools'; the same runner image had passed the day before, so the SDK repository changed, not Ferry. CI now installs onlyplatform-tools, and setup-android v4 still has the old default. - Observed (2026-09-15): upgrade from 1.0.0 to 1.0.1 followed the 1.0.1 notes on an arm64 macOS host, in an isolated Compose project on loopback. A device, a text, a 200 KB file and an access password were created on 1.0.0; the notes' backup, image change, pull and
up -dthen ran. On 1.0.1 the device token still worked, the message list was identical, the downloaded file matched its SHA-256, a wrong password was refused (401) and the right one joined (201), and the log showed the new container-listener line. TheTZline left in the oldcompose.yamlcaused no problem. Not covered: restoring that backup, a phone, and amd64.
- Observed (2026-10-13):
v1.0.2points to209e477, released by run 38364727661 from CI run 38363293046 undersha256:43daceb1059f0e3d287141e6372d0699d8aa6b23e30a1defc788735db3bf471a. Max authorized the release and its publishing on 2026-10-13. Outside CI, an anonymous manifest request returned that digest and the Compose download URL answered anonymously. - Observed (2026-10-13): the Server and Web change is the download link (
docs/web-download-links.md); desktop Chrome saved a downloaded file with its original SHA-256 from an image built at8371f22. Max waived the Android in-app browser check. - Observed (2026-10-13): the maintainer's own deployment moved from 1.0.0 through a local
mainbuild to the published 1.0.2 image after ascripts/ferry-data.shbackup, on the same volume; the container reported revision209e477and served the new Web app.
Three new users will each deploy Ferry from the README on their own. Record how long each deployment took and where each person got stuck. Ferry collects no telemetry for this.