From 253c72d2b78319020ec40c29f8d987d877545080 Mon Sep 17 00:00:00 2001 From: Jaap-Jan de Wit | DodoTech Date: Wed, 5 Aug 2026 12:51:19 +0200 Subject: [PATCH] Name the organisation the repository actually lives in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It moved to DodoTech-Public, and every address in the product still said DodoTech. That looked like it worked, which is the part worth writing down: Gitea leaves a 301 at the old path and HttpClient follows a redirect on a GET, so both update channels would have kept polling through it. What a 301 does not survive is a POST. `vpk upload gitea` publishes the desktop release by POSTing to that URL, so the stale address would have failed at the one step the whole feature depends on — and a redirect is a thing an operator can delete, which turns "works today" into the same silent outage this session has already spent two commits on. So both channel constants, both release scripts, the workflow's REPO, the image's source label and the curl in phase 16 all name the live path. The local remote too, which had been printing a redirect warning on every push. Measured after the move: the org, the repo and the nightly release all answer 200 anonymously, and that release now carries both assets — the manifest and a 54 MB APK. The upload going through also answers the open question about the reverse proxy's body-size limit, which nothing local could test. --- .github/workflows/ci.yml | 2 +- README.md | 2 +- docs/adr/0013-desktop-distribution-and-updates.md | 2 +- docs/manual-checks.md | 2 +- scripts/release-android.ps1 | 2 +- scripts/release-windows.ps1 | 6 +++++- src/DodoSSH.Api/Dockerfile | 2 +- .../Platform/AndroidUpdateChannel.cs | 9 ++++++++- .../Platform/VelopackUpdateChannel.cs | 12 +++++++++++- 9 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfe68a6..ad6a700 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -517,7 +517,7 @@ jobs: if: github.ref == 'refs/heads/main' env: FORGE: https://git.dodotech.cloud - REPO: DodoTech/DodoSSH + REPO: DodoTech-Public/DodoSSH TOKEN: ${{ secrets.GITHUB_TOKEN }} STAGED: ${{ steps.nightly.outputs.staged }} VERSION: ${{ steps.nightly.outputs.version }} diff --git a/README.md b/README.md index 7715182..1109369 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ not go. If the repository is private, every check answers `404`, and the honest about that is say so. It is one line to check: ```bash -curl -so /dev/null -w '%{http_code}\n' https://git.dodotech.cloud/api/v1/repos/DodoTech/DodoSSH +curl -so /dev/null -w '%{http_code}\n' https://git.dodotech.cloud/api/v1/repos/DodoTech-Public/DodoSSH ``` `200` and updates work. `404` and they cannot. `/api/v1/version` answering `200` proves only that the forge diff --git a/docs/adr/0013-desktop-distribution-and-updates.md b/docs/adr/0013-desktop-distribution-and-updates.md index ed77c73..2f90ddc 100644 --- a/docs/adr/0013-desktop-distribution-and-updates.md +++ b/docs/adr/0013-desktop-distribution-and-updates.md @@ -118,7 +118,7 @@ than assumed: - **The feed repository must be readable with no credentials, and that is a deployment precondition** rather than a property of the code. It is checkable in one line, which is the only reason it is worth stating: - `curl -so /dev/null -w '%{http_code}' https://git.dodotech.cloud/api/v1/repos/DodoTech/DodoSSH` answers + `curl -so /dev/null -w '%{http_code}' https://git.dodotech.cloud/api/v1/repos/DodoTech-Public/DodoSSH` answers `200` when the design holds and `404` when it does not. `/api/v1/version` answering `200` proves only that the forge is reachable, which is what made this look like nothing was wrong. - **An unreachable channel must be distinguishable from a current build.** `IUpdateChannel.CheckAsync` used diff --git a/docs/manual-checks.md b/docs/manual-checks.md index ce5b60a..9672a93 100644 --- a/docs/manual-checks.md +++ b/docs/manual-checks.md @@ -1802,7 +1802,7 @@ Nothing else in this phase or the next means anything until this passes. Run it nothing: ```bash -curl -so /dev/null -w '%{http_code}\n' https://git.dodotech.cloud/api/v1/repos/DodoTech/DodoSSH +curl -so /dev/null -w '%{http_code}\n' https://git.dodotech.cloud/api/v1/repos/DodoTech-Public/DodoSSH ``` **Pass:** `200`. diff --git a/scripts/release-android.ps1 b/scripts/release-android.ps1 index 33ab73f..77b366e 100644 --- a/scripts/release-android.ps1 +++ b/scripts/release-android.ps1 @@ -50,7 +50,7 @@ $RepoRoot = Split-Path -Parent $PSScriptRoot $Project = Join-Path $RepoRoot 'src/DodoSSH.Client.Android/DodoSSH.Client.Android.csproj' $Staging = Join-Path $RepoRoot 'artifacts/android' $Forge = 'https://git.dodotech.cloud' -$Repo = 'DodoTech/DodoSSH' +$Repo = 'DodoTech-Public/DodoSSH' $Api = "$Forge/api/v1/repos/$Repo" function Write-Step([string] $Message) { diff --git a/scripts/release-windows.ps1 b/scripts/release-windows.ps1 index 3035328..38f3073 100644 --- a/scripts/release-windows.ps1 +++ b/scripts/release-windows.ps1 @@ -62,7 +62,11 @@ $PackAuthors = 'DodoTech' # The project's own forge. Never a DodoSSH deployment — ADR 0011 rule 2. The same URL is a constant in # VelopackUpdateChannel, and the two have to agree or the client polls somewhere nothing is published. -$RepoUrl = 'https://git.dodotech.cloud/DodoTech/DodoSSH' +# +# The owner is part of it. When the repository moved organisations Gitea left a 301 at the old path, which +# a GET follows and an upload does not — so a stale URL here fails at the one step that matters rather than +# redirecting quietly. +$RepoUrl = 'https://git.dodotech.cloud/DodoTech-Public/DodoSSH' # A contract with VelopackUpdateChannel.ReleaseChannel. It is Velopack's Windows default, so leaving it # unsaid on both sides would work too — but unsaid here and stated there is how a feed goes quiet with no diff --git a/src/DodoSSH.Api/Dockerfile b/src/DodoSSH.Api/Dockerfile index 4a583af..4118a0c 100644 --- a/src/DodoSSH.Api/Dockerfile +++ b/src/DodoSSH.Api/Dockerfile @@ -111,7 +111,7 @@ LABEL org.opencontainers.image.title="DodoSSH API" \ org.opencontainers.image.description="DodoSSH server: sync, identity, teams and relay authorization." \ org.opencontainers.image.vendor="DodoTech" \ org.opencontainers.image.licenses="MIT" \ - org.opencontainers.image.source="https://git.dodotech.cloud/DodoTech/DodoSSH" \ + org.opencontainers.image.source="https://git.dodotech.cloud/DodoTech-Public/DodoSSH" \ org.opencontainers.image.version="${VERSION}" \ org.opencontainers.image.revision="${REVISION}" \ org.opencontainers.image.created="${CREATED}" diff --git a/src/DodoSSH.Client.Android/Platform/AndroidUpdateChannel.cs b/src/DodoSSH.Client.Android/Platform/AndroidUpdateChannel.cs index cc4941e..32b8381 100644 --- a/src/DodoSSH.Client.Android/Platform/AndroidUpdateChannel.cs +++ b/src/DodoSSH.Client.Android/Platform/AndroidUpdateChannel.cs @@ -76,7 +76,14 @@ internal sealed partial class ForgeJsonContext : JsonSerializerContext; internal sealed class AndroidUpdateChannel : IUpdateChannel { /// The project's own forge, and the one address in this file. - private const string RepositoryApi = "https://git.dodotech.cloud/api/v1/repos/DodoTech/DodoSSH"; + /// + /// ◆ The owner is DodoTech-Public and that half of the path matters. The repository was moved + /// between organisations, and Gitea leaves a 301 at the old one — so a client still naming it looks + /// fine, because HttpClient follows a redirect on a GET. What it buys is a dependency on a + /// redirect somebody can delete, and it does not extend to the release scripts, whose uploads are + /// POSTs. The live path is named here, in VelopackUpdateChannel and in both scripts. + /// + private const string RepositoryApi = "https://git.dodotech.cloud/api/v1/repos/DodoTech-Public/DodoSSH"; /// /// The session name the installer writes under, and it is reused rather than made unique. diff --git a/src/DodoSSH.Client.App/Platform/VelopackUpdateChannel.cs b/src/DodoSSH.Client.App/Platform/VelopackUpdateChannel.cs index 723306a..71d5ead 100644 --- a/src/DodoSSH.Client.App/Platform/VelopackUpdateChannel.cs +++ b/src/DodoSSH.Client.App/Platform/VelopackUpdateChannel.cs @@ -75,14 +75,24 @@ internal sealed class VelopackUpdateChannel : IUpdateChannel /// /// /// This must never become a setting. ADR 0011 rule 2 says the deployment a client signs in to + /// /// is never where the client comes from, and it says the same about the update check: an operator who /// can answer "is there a newer version" can answer "no" forever, and pin a chosen user to a build /// with a known hole without holding any key. A configurable feed URL is exactly the knob that would /// hand them that, whether through a settings screen or through somebody editing the plaintext /// settings.json by hand. A constant is that rule expressed structurally rather than as a convention /// somebody has to keep. + /// + /// + /// ◆ DodoTech-Public, and the owner is part of the address rather than incidental. The + /// repository was moved between organisations, and Gitea leaves a 301 behind at the old path — which is + /// why a client pointing at the old one appears to work: HttpClient follows a redirect on a GET. + /// It does not follow one on a POST, so vpk upload against the stale URL fails rather than + /// redirecting, and a redirect is a thing an operator can remove. Both heads and both release scripts + /// name the live path. + /// /// - private const string RepositoryUrl = "https://git.dodotech.cloud/DodoTech/DodoSSH"; + private const string RepositoryUrl = "https://git.dodotech.cloud/DodoTech-Public/DodoSSH"; /// /// The release channel to read, and it is stated rather than left to the default.