Plan step 17: web access on a Managed Webspace, alongside the sandbox

Keeping both halves in one step: a bubblewrap runtime alone would only
prove that sandboxed builds work somewhere, and web access alone would
mean builds running unsandboxed on the webspace. Neither ships value on
its own, so step 17 now covers the whole deployment.

The web half needs no code: Hostsharing provides Apache plus Let's
Encrypt and documents the reverse proxy to a self-hosted service, so the
managed nginx container of ADR 0005 is not used there. What it needs is
the booked "eigener Serverdienst" option with an assigned localhost port,
a systemd user unit (which `init --systemd` already generates), and a
`.htaccess` with a `[proxy]` RewriteRule — with sources from Hostsharing's
own wiki and feature pages. GitTally fits as is, because it builds
external links from `server.publicBaseUrl` rather than from the request,
so no forward-headers handling is required.

Two points are explicitly marked unverified in the step file: the
effective AllowOverride value and whether an unassigned port would bind.
Also ticks steps 15 and 16 in the plan index — both carry a Result
section and are long done.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-08-11 08:37:58 +02:00
co-authored by Claude
parent 2351ddcecd
commit d5f2784b60
2 changed files with 56 additions and 5 deletions
+53 -2
View File
@@ -1,9 +1,14 @@
# Step 17: Bubblewrap Build Runtime for Managed Webspaces
# Step 17: Running GitTally on a Managed Webspace (bubblewrap builds + web access)
Prerequisites: steps 11, 15, 16.
Read `README.md` first.
Motivated by running GitTally on Hostsharing **Managed Webspaces**: no root, no Docker daemon, but `bwrap` (bubblewrap) is available and unprivileged user namespaces are allowed.
Target use case: GitTally builds GitTally itself on a Managed Webspace; builds needing special dependencies get them from a prepared root filesystem instead of the host.
Projects that need Docker for their own tests (hs.hsadmin.ng with Testcontainers) stay on a container host like vm4006 — the webspace is for Docker-free builds only.
The step covers two halves of the same deployment and is deliberately not split:
the build sandbox (most of this document) and the web access under a domain (last section).
Without the second half the first one only proves that sandboxed builds work somewhere; without the first one GitTally on a webspace would run builds unsandboxed on the host.
## Precondition Check (run on the target webspace first)
@@ -71,6 +76,51 @@ bwrap --unshare-user --unshare-pid --die-with-parent --uid 0 --gid 0 \
- No Docker inside the sandbox, so no Testcontainers-based tests; build commands must select a Docker-free test subset.
For GitTally's own build this means `TestcontainersSmokeTest` must become conditional (`enabledIf` docker present) — that change is part of this step.
## Web Access under a Domain (no Docker, no managed nginx)
The managed nginx/TLS container from ADR 0005 is for container hosts without a reverse proxy.
A Managed Webspace does not need it: the platform provides Apache plus Let's Encrypt, and documents the reverse proxy to a self-hosted service.
Three platform-side prerequisites, none of them code:
1. **Book the "eigener Serverdienst" option** — a service user plus one reserved localhost port, requested from `service@hostsharing.net` stating the service user and the number of ports.
Surcharged on Managed Webspaces (RAM contingent in 128 MB steps), included on Managed Servers.
The port number is **assigned by Hostsharing** (wiki examples use 34567, 38005/38006), so it goes into `server.port` — GitTally's 18080 is not available by choice.
Sources: [Individuelle Serverdienste](https://www.hostsharing.net/features/individuelle-serverdienste/), [Apache](https://www.hostsharing.net/features/apache/).
2. **Run the service as a systemd user unit** — mandatory on Managed Webspaces (no `nohup`, no supervisord); lingering needs a valid login shell configured in HSAdmin, and the account's RAM is capped by a slice (`systemctl status pacs-<account>.slice`).
`gittally init --systemd` already generates the unit and the `gittally.env`, whose `JAVA_OPTS=-Xmx…` is what keeps the JVM inside the slice.
Source: [Prozessmanagement mit systemd im Userspace](https://wiki.hostsharing.net/index.php/Prozessmanagement_mit_systemd_im_Userspace).
3. **Let's Encrypt** is a domain option ticked in HSAdmin (free, automatic, includes the wildcard subdomain; requires the domain's nameservers to be delegated to Hostsharing), so TLS terminates in the managed Apache.
Source: [TLS](https://www.hostsharing.net/doc/managed-operations-platform/tls/).
The proxy itself is one `.htaccess` in `~/doms/<domain>/htdocs-ssl/`, following Hostsharing's own Mattermost and Tomcat guides:
```apache
DirectoryIndex disabled
RewriteEngine On
RewriteBase /
RewriteRule .* http://127.0.0.1:<assigned-port>%{REQUEST_URI} [proxy]
```
Sources: [Mattermost Installieren](https://wiki.hostsharing.net/index.php/Mattermost_Installieren), [Tomcat Installieren](https://wiki.hostsharing.net/index.php?title=Tomcat_Installieren).
The matching GitTally configuration:
```yaml
server:
port: <assigned-port>
bindAddress: 127.0.0.1 # the default since v0.9.9 — exactly right here
publicBaseUrl: "https://ci.example.de/" # used for every link posted to Gitea
nginx:
enabled: false # the managed nginx container is not used on a webspace
```
**This half needs no code change.** GitTally never reconstructs absolute URLs from the request — everything external comes from `server.publicBaseUrl` and the UI links relatively — so the usual reverse-proxy fix `server.forward-headers-strategy` is not needed.
Two claims could **not** be verified from a Hostsharing primary source; check them on the target webspace rather than relying on them:
- the effective `AllowOverride` value — that `RewriteRule [P]`, `DirectoryIndex` and `RequestHeader` work in user `.htaccess` is evidenced by the wiki guides, but the literal token is undocumented;
- whether an unassigned high port would bind at all — the documented contract is to use the assigned ones.
## ADR
Write ADR 0007: bubblewrap user-namespace sandbox as the third build runtime (options considered: bwrap (chosen), proot/fakechroot (slow, fragile), plain native with hand-installed toolchains (no isolation, host pollution)).
@@ -86,4 +136,5 @@ Write ADR 0007: bubblewrap user-namespace sandbox as the third build runtime (op
- The precondition command line above passes on the target webspace; its output is recorded in this file.
- `./gradlew ktlintFormat` then `./gradlew build` is green — also on a machine without Docker (Testcontainers smoke test skipped, not failed).
- On a Managed Webspace: GitTally (from the runtime bundle) builds a real branch of a repo inside the bwrap sandbox; git commands work in the worktree; `.git/gittally/` is not readable from the build; a write to `/usr` fails.
- Docs updated: `docs/configuration.md` (bwrap section), architecture skill (third runtime), ADR 0007.
- On the same webspace: the UI answers over HTTPS under the domain through the Apache `.htaccess` proxy, the service survives a logout and a reboot (systemd lingering), and Gitea statuses carry `publicBaseUrl` links that resolve.
- Docs updated: `docs/configuration.md` (bwrap section), architecture skill (third runtime), ADR 0007, and `docs/deployment.md` gains "Hostsharing Managed Webspace" as a third deployment variant — written only once the setup above is verified on a real webspace, not from this plan.
+3 -3
View File
@@ -75,12 +75,12 @@ Added after the 2026-08-10 overhead measurements on vm2176:
Added for the vm2176 → vm4006 migration (2026-08-10):
- [ ] `15-runtime-bundle-distribution.md` — self-contained runtime bundle (jlink JRE + jar) for hosts without a Java runtime
- [ ] `16-git-in-docker-builds.md` — read-only git metadata inside Docker build containers, with `.git/gittally/` masked
- [x] `15-runtime-bundle-distribution.md` — self-contained runtime bundle (jlink JRE + jar) for hosts without a Java runtime
- [x] `16-git-in-docker-builds.md` — read-only git metadata inside Docker build containers, with `.git/gittally/` masked
Added for running GitTally on Hostsharing Managed Webspaces (2026-08-10):
- [ ] `17-bwrap-build-runtime.md` — bubblewrap user-namespace build sandbox with a prepared rootfs, for hosts without Docker (precondition check first — see the step file)
- [ ] `17-bwrap-build-runtime.md` GitTally on a Managed Webspace: bubblewrap user-namespace build sandbox with a prepared rootfs (precondition check first — see the step file), plus web access under a domain via the platform's Apache proxy and Let's Encrypt
Steps 0103 are independent of each other.
Steps 0406 depend on 0103.