Add support for Tranquil PDS #78

Closed
opened 2026-07-26 00:08:23 +00:00 by Grandiras · 2 comments
Owner

Besides the reference PDS from bluesky, also support Tranquil PDS for hosting.

Besides the reference PDS from bluesky, also support Tranquil PDS for hosting.
Collaborator

🤖 On it — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 On it — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/95); the reply will link the full session transcript. 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/issue-78/run-95.md) — updating every ~5 minutes while the task runs.
Collaborator

Pushed to claude/issue-78.

What I did

Tranquil PDS is now supported alongside the reference Bluesky PDS, for both hosting and administration. I read the upstream source (tranquil.farm/tranquil-pds) rather than working from the README, because two of its behaviours change the design.

Hosting — ATProtoNet.Aspire.Hosting

AddAtProtoTranquilPds(name, port?, tag?) adds the atcr.io/tranquil.farm/tranquil-pds container, and WithAtProtoTranquilPds(pds) wires a project to it — the same shape as the existing AddAtProtoPds / WithAtProtoPds pair. Tranquil stores its repositories in PostgreSQL, so the call also provisions a server and database and waits on them; WithDatabase / WithDatabaseUrl point it at one you already have and drop the generated resources from the model. It's handed a postgres:// URI, not the ADO.NET connection string Aspire's PostgresDatabaseResource exposes — that one wouldn't parse. Plus the usual With* configuration surface (hostname, handle domains, secrets, blob storage incl. S3, PLC, crawlers, mail, report service).

Administration — PdsAdminClient

This is the difference that matters. The reference PDS has one server-wide admin password used with HTTP Basic; Tranquil has no such password and instead flags individual accounts as administrators, authenticated with an ordinary session token. PdsAdminOptions.Authentication (new PdsAdminAuthentication enum, default unchanged) selects the scheme, AdminIdentifier names the account. Under AdminAccount the client signs in lazily, reuses the session, and re-authenticates once if the server rejects it — which a long-lived typed HttpClient registration needs. AddAtProtoPdsAdmin() binds the new keys and fails at host-build time if the mode is set without an identifier.

Two upstream behaviours worth flagging

  • The administrator account is deliberately not created for you. Tranquil marks the first account on an empty instance as an administrator, so the app registers it once with CreateAccountAsync (signup is public, so it works before the client has any authority). The handle is derived from the hostname so WithHostname alone leaves a valid one, and defaults to pdsadminadmin is on Tranquil's reserved-subdomain list and would be rejected at signup.
  • Running locally the container gets development relaxations, none of them when publishing. Two are load-bearing: an empty Tranquil mints a bootstrap invite code only to its log, and login is blocked until an account has a verified comms channel. At Tranquil's own defaults, no account could be created on a local instance and none could sign in. WithDevelopmentMode(false) turns the set off.

Verified

  • dotnet build — succeeds, 0 warnings (the repo treats CS1591 as an error on this package).
  • dotnet test tests/ATProtoNet.Tests/ --configuration Release1162 passed, 0 failed, 5 gated integration tests skipped as before. 59 of those tests are new (45 hosting, 11 admin-auth, 3 DI).
  • Published a real Aspire manifest from a throwaway AppHost and inspected it: the container, PostgreSQL server/database, postgresql:// DATABASE_URL, generate blocks for every secret, AdminIdentifier correctly resolving to pdsadmin.{pds-hostname.value}, and no development-mode variables leaking into the published output.
  • dotnet pack — the package declares Aspire.Hosting.PostgreSQL 13.4.6 correctly.

Not verified: I could not actually start the container. atcr.io requires authentication (docker login atcr.io) and this environment has no container runtime, so the run-mode path is verified at the application-model and manifest level only. Everything about the live container — that the env vars land as intended, that the health check answers — is untested against a running server. Documented the registry login requirement in docs/managed-pds.md.

Docs updated: new Tranquil PDS section in docs/managed-pds.md, plus docs/aspire.md, docs/api-reference.md, docs/architecture.md, README.md, and CHANGELOG.md under [Unreleased].


⏱ 22m 10s · 136 turns · tokens: 231.3k in / 101.4k out (+17744.1k cache-read)
📜 Full session transcript
→ Opened PR: #79

Pushed to **`claude/issue-78`**. ## What I did Tranquil PDS is now supported alongside the reference Bluesky PDS, for both hosting and administration. I read the upstream source (`tranquil.farm/tranquil-pds`) rather than working from the README, because two of its behaviours change the design. **Hosting — `ATProtoNet.Aspire.Hosting`** `AddAtProtoTranquilPds(name, port?, tag?)` adds the `atcr.io/tranquil.farm/tranquil-pds` container, and `WithAtProtoTranquilPds(pds)` wires a project to it — the same shape as the existing `AddAtProtoPds` / `WithAtProtoPds` pair. Tranquil stores its repositories in PostgreSQL, so the call also provisions a server and database and waits on them; `WithDatabase` / `WithDatabaseUrl` point it at one you already have and drop the generated resources from the model. It's handed a `postgres://` URI, not the ADO.NET connection string Aspire's `PostgresDatabaseResource` exposes — that one wouldn't parse. Plus the usual `With*` configuration surface (hostname, handle domains, secrets, blob storage incl. S3, PLC, crawlers, mail, report service). **Administration — `PdsAdminClient`** This is the difference that matters. The reference PDS has one server-wide admin password used with HTTP Basic; Tranquil has no such password and instead flags individual *accounts* as administrators, authenticated with an ordinary session token. `PdsAdminOptions.Authentication` (new `PdsAdminAuthentication` enum, default unchanged) selects the scheme, `AdminIdentifier` names the account. Under `AdminAccount` the client signs in lazily, reuses the session, and re-authenticates once if the server rejects it — which a long-lived typed `HttpClient` registration needs. `AddAtProtoPdsAdmin()` binds the new keys and fails at host-build time if the mode is set without an identifier. **Two upstream behaviours worth flagging** - The administrator account is deliberately *not* created for you. Tranquil marks the first account on an empty instance as an administrator, so the app registers it once with `CreateAccountAsync` (signup is public, so it works before the client has any authority). The handle is *derived* from the hostname so `WithHostname` alone leaves a valid one, and defaults to `pdsadmin` — `admin` is on Tranquil's reserved-subdomain list and would be rejected at signup. - Running locally the container gets development relaxations, none of them when publishing. Two are load-bearing: an empty Tranquil mints a bootstrap invite code only to its log, and login is blocked until an account has a verified comms channel. At Tranquil's own defaults, no account could be created on a local instance and none could sign in. `WithDevelopmentMode(false)` turns the set off. ## Verified - `dotnet build` — succeeds, **0 warnings** (the repo treats CS1591 as an error on this package). - `dotnet test tests/ATProtoNet.Tests/ --configuration Release` — **1162 passed, 0 failed**, 5 gated integration tests skipped as before. 59 of those tests are new (45 hosting, 11 admin-auth, 3 DI). - Published a real Aspire manifest from a throwaway AppHost and inspected it: the container, PostgreSQL server/database, `postgresql://` `DATABASE_URL`, `generate` blocks for every secret, `AdminIdentifier` correctly resolving to `pdsadmin.{pds-hostname.value}`, and no development-mode variables leaking into the published output. - `dotnet pack` — the package declares `Aspire.Hosting.PostgreSQL` 13.4.6 correctly. **Not verified:** I could not actually start the container. `atcr.io` requires authentication (`docker login atcr.io`) and this environment has no container runtime, so the run-mode path is verified at the application-model and manifest level only. Everything about the *live* container — that the env vars land as intended, that the health check answers — is untested against a running server. Documented the registry login requirement in `docs/managed-pds.md`. Docs updated: new **Tranquil PDS** section in `docs/managed-pds.md`, plus `docs/aspire.md`, `docs/api-reference.md`, `docs/architecture.md`, `README.md`, and `CHANGELOG.md` under `[Unreleased]`. --- ⏱ 22m 10s · 136 turns · tokens: 231.3k in / 101.4k out (+17744.1k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/ATProto.NET/issue-78/run-95.md) → Opened PR: https://git.grandiras.net/Grandiras/ATProto.NET/pulls/79
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Grandiras/ATProto.NET#78
No description provided.