gitea leaves the baseline and becomes an app-store install

It was in both light profiles on the reasoning that it fronts a REMOTE instance and so
needs nothing installed locally. That is true and it was beside the point: a baseline
process appears in the dock and in the Permissions screen whether or not anyone ever
gave it a URL, so a fresh server offered to grant members access to a Gitea that did
not exist. "Is Gitea here" had two answers that could disagree.

Now it is `existing` mode with a URL and a token, like any other remote service, and
the one place that says whether it is here is the install row. No compose template and
no `provisioned` mode: Gitea is always something the owner already runs, and offering to
spin one up would mean owning its migration, backup and upgrade story.

members: 'none' — not because Gitea is single-tenant, it is the most per-user service
in the catalogue, but because there is nothing for the INSTALLER to do. The owner's
connection carries the instance; each member adds their own access token from /gitea and
acts only as themselves upstream. A provisioner would need an admin token and would mint
credentials on their behalf, which is more authority than this needs.

The catalogue test already pinned "the store offers exactly what light leaves out", so
removing it from the profile is what forced the entry to exist. Both light profiles
changed together — the mac one carried the same comment and the same gap.

Permissions on a fresh install is now Files and Plans. Plans stays because it reads the
platform's own shipped markdown from <repo>/plans, not anyone's disk, so it needs
nothing installed and exposes nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-11 18:17:47 +00:00
co-authored by Claude Opus 5
parent e393d0f5c2
commit 6b4fed68fd
3 changed files with 40 additions and 10 deletions
+6 -5
View File
@@ -26,16 +26,17 @@ module.exports = defineProfile({
'officer-agent', // spawns `claude` — chat is dead without it 'officer-agent', // spawns `claude` — chat is dead without it
'officer-opencode', // the alternative agent 'officer-opencode', // the alternative agent
'officer-pty', // the terminal 'officer-pty', // the terminal
// Included even though a light host runs no Gitea: this sidecar fronts a REMOTE instance. Its URL
// and token live in `service_connections`, set from /gitea, so it needs nothing installed here.
// That is what separates it from the sidecars below, which supervise a local daemon or container.
// Same reasoning as the mac light profile, which has included it since it was added.
'officer-gitea',
], ],
// Excluded by CHOICE rather than by platform limits — every one of these would run on a Linux host. // Excluded by CHOICE rather than by platform limits — every one of these would run on a Linux host.
// A light install simply is not running the thing behind it. // A light install simply is not running the thing behind it.
excluded: { excluded: {
// Was in the baseline until 2026-08-11, on the reasoning that it fronts a REMOTE instance and so needs
// nothing installed locally. True, and beside the point: a baseline process appears in the Permissions
// screen and the dock whether or not anyone has given it a URL, so a fresh server offered to grant Gitea
// access to an instance that did not exist. It is installable now — `existing` mode, URL and token — which
// makes "is Gitea here" one question with one answer instead of two that disagree.
'officer-gitea': 'fronts a remote instance; installed from the app store with its URL and token',
'officer-vnc': 'no desktop to mirror on a light install', 'officer-vnc': 'no desktop to mirror on a light install',
'officer-email': 'needs the mbsync/IMAP stack the light profile does not install', 'officer-email': 'needs the mbsync/IMAP stack the light profile does not install',
'officer-music': 'the ffprobe indexer works, but a full library index is not a light-install concern', 'officer-music': 'the ffprobe indexer works, but a full library index is not a light-install concern',
+6 -5
View File
@@ -32,17 +32,18 @@ module.exports = defineProfile({
// The terminal. Runs under node rather than bun — node-pty binds a native addon built against // The terminal. Runs under node rather than bun — node-pty binds a native addon built against
// node's ABI. That detail lives in ecosystem.config.cjs, not here. // node's ABI. That detail lives in ecosystem.config.cjs, not here.
'officer-pty', 'officer-pty',
// Included even though a laptop hosts no Gitea: this sidecar fronts a REMOTE instance. The URL and
// its token live in `service_connections`, set from /gitea, so it points at gitea.pastilhas.dev
// over the network and needs nothing installed here. That is what separates it from the sidecars
// below that supervise a local daemon or container.
'officer-gitea',
], ],
excluded: { excluded: {
// Cannot run on macOS at all. // Cannot run on macOS at all.
'officer-vnc': 'mirrors an Xorg display with x11vnc; macOS has no Xorg', 'officer-vnc': 'mirrors an Xorg display with x11vnc; macOS has no Xorg',
// Left the baseline on 2026-08-11, on both light profiles together. It genuinely needs nothing installed
// locally — it points at a remote instance over the network — but a baseline process shows up in the dock
// and the Permissions screen whether or not a URL was ever given, so "is Gitea here" had two answers. It
// is an app-store install now: `existing` mode, URL and token, same as any other remote service.
'officer-gitea': 'fronts a remote instance; installed from the app store with its URL and token',
// Would run, but needs something setup_mac_light.sh deliberately does not install. // Would run, but needs something setup_mac_light.sh deliberately does not install.
'officer-email': 'needs the mbsync/IMAP stack setup_mac_light.sh does not install', 'officer-email': 'needs the mbsync/IMAP stack setup_mac_light.sh does not install',
'officer-caldav': 'supervises Radicale, which setup_mac_light.sh does not install', 'officer-caldav': 'supervises Radicale, which setup_mac_light.sh does not install',
+28
View File
@@ -331,6 +331,34 @@ export const CATALOGUE: CatalogueEntry[] = [
composeTemplate: 'radicale', composeTemplate: 'radicale',
existingFields: [{ key: 'url', label: 'CalDAV URL', type: 'url', required: true }], existingFields: [{ key: 'url', label: 'CalDAV URL', type: 'url', required: true }],
}, },
{
id: 'gitea',
ui: { name: 'Gitea', icon: 'GitBranch', color: '#34d399', rootRoute: '/gitea', routes: ['/gitea'] },
process: 'officer-gitea',
label: 'Gitea',
summary: 'Repositories, issues and pull requests from your own Gitea account',
// 'none' because there is nothing for the INSTALLER to provision, not because Gitea is single-tenant —
// it is the most per-user service here. The owner's connection carries the instance URL; every member
// adds their own access token from /gitea and acts only as themselves upstream, with Gitea's own
// permissions as the second and real gate. A provisioner could not do that part for them without
// holding an admin token and minting tokens on their behalf, which is more authority than this needs.
members: 'none',
// `existing` only, and there is no compose template. Gitea here is always something the owner already
// runs — on this machine, on another, or hosted. Provisioning one would mean owning the migration,
// backup and upgrade story for a service that is nobody's side feature.
modes: ['existing'],
capability: 'gitea',
existingFields: [
{ key: 'url', label: 'Gitea URL', type: 'url', required: true, placeholder: 'https://gitea.example.com' },
{
key: 'secret',
label: 'Your access token',
type: 'secret',
required: true,
help: 'A personal access token from your own Gitea account. Members add their own from /gitea.',
},
],
},
{ {
id: 'headscale', id: 'headscale',
ui: { name: 'Headscale', icon: 'Network', color: '#818cf8', rootRoute: '/headscale', routes: ['/headscale'] }, ui: { name: 'Headscale', icon: 'Network', color: '#818cf8', rootRoute: '/headscale', routes: ['/headscale'] },