From f918a90c3bdf1827c5ccd2f621067780dd744ca9 Mon Sep 17 00:00:00 2001 From: reachableceo Date: Tue, 1 Sep 2026 19:02:51 -0500 Subject: [PATCH] feat: add Rathole Cloudron package (Infrastructure) [#650] Server-mode Rathole 0.5.0 as the 11th package: pinned upstream binary (sha256 gate) on cloudron/base:4.0.0, control port 2333 plus a 100-port tunnel exit range, hot-reloading config in /app/data, auth-proxy verdict (no user concept). Verified end-to-end with a live client tunnel. Docs gardened (STATUS/README/JOURNAL to 11 packages). Ticket: https://projects.knownelement.com/issues/650 --- JOURNAL.md | 74 +++++++++++++- .../Infrastructure/rathole/.dockerignore | 1 + .../Infrastructure/rathole/.env.example | 8 ++ .../Infrastructure/rathole/CHANGELOG.md | 27 +++++ .../rathole/CloudronManifest.json | 43 ++++++++ .../Infrastructure/rathole/Dockerfile | 48 +++++++++ .../Infrastructure/rathole/README.md | 95 ++++++++++++++++++ .../Infrastructure/rathole/logo.png | Bin 0 -> 11463 bytes .../Infrastructure/rathole/start.sh | 54 ++++++++++ .../Infrastructure/rathole/status.html | 51 ++++++++++ README.md | 11 +- STATUS.md | 16 +-- 12 files changed, 415 insertions(+), 13 deletions(-) create mode 100644 Package-Workspace/Infrastructure/rathole/.dockerignore create mode 100644 Package-Workspace/Infrastructure/rathole/.env.example create mode 100644 Package-Workspace/Infrastructure/rathole/CHANGELOG.md create mode 100644 Package-Workspace/Infrastructure/rathole/CloudronManifest.json create mode 100644 Package-Workspace/Infrastructure/rathole/Dockerfile create mode 100644 Package-Workspace/Infrastructure/rathole/README.md create mode 100644 Package-Workspace/Infrastructure/rathole/logo.png create mode 100755 Package-Workspace/Infrastructure/rathole/start.sh create mode 100644 Package-Workspace/Infrastructure/rathole/status.html diff --git a/JOURNAL.md b/JOURNAL.md index d744f13..8f6e5ae 100644 --- a/JOURNAL.md +++ b/JOURNAL.md @@ -4,7 +4,7 @@ **Project**: TSYSDevStack-SupportStack-Cloudron **Goal**: Package ~57 applications for Cloudron PaaS platform **Start Date**: 2025-01-24 -**Current Status**: 10/~57 packages completed (~18%) +**Current Status**: 11/~57 packages completed (~19%) ## Completed Packages @@ -823,6 +823,78 @@ Dockerfile --- +### 11. Rathole (Infrastructure) ✅ +**Date**: 2026-09-01 +**Application**: Rathole — secure, high-performance reverse proxy for NAT +traversal (frp/ngrok class, Rust); this package runs the **server** side +**Package Size**: 3.51GB (cloudron/base 4.0.0 dominates) +**Ports**: 8000 (HTTP status), 2333 (control), 5200-5299 (tunnel exits) +**Addons**: localstorage (auth proxy, no database) + +**Key Learnings**: +- **Auth gate verdict**: NO user concept — no UI, no accounts, no SSO + hooks. Tunnels are authorized by mandatory per-service tokens + (random `default_token` seeded on first run), with optional Noise/TLS + transport encryption → `httpAuth.type = proxy` gates the only HTTP + surface (a static status page), the draw.io/Easy-Gate pattern for + user-less apps +- **Base-image glibc trap**: upstream dropped musl release builds in + v0.5.0 (TLS linking burden); the only linux/amd64 asset is + `x86_64-unknown-linux-gnu`, built on ubuntu-latest → needs glibc + >= 2.35 → `cloudron/base:4.0.0` (22.04). The 3.2.0 (20.04) base used + by earlier packages is too old for this binary +- **Strict TOML schema quirks**: v0.5.0 rejects `nodelay` directly under + `[server.transport]` (must nest under `[server.transport.tcp]`) and + rejects a server config with **zero** `[server.services.*]` blocks — + the seed config must ship one active placeholder service +- **Lazy service binding**: the server binds a service's exit port only + when its client registers — an empty listener table is normal until a + client connects +- **tcpPorts ranges**: CloudronManifest `portCount` allocates sequential + ports from `defaultValue` (max 1000) with `containerPort` bridging — + used for a 100-port tunnel exit range (5200-5299) that survives + admin-chosen external renumbering without touching server.toml +- **httpPort is required** (healthCheckPath too) even for headless TCP + apps: a tiny `python3 -m http.server` sidecar on the status page + satisfies both and gives the auth proxy something to gate + +**Build Process**: +- Upstream `rathole-org/rathole` v0.5.0 (rapiz1 redirects) release zip + downloaded in-Dockerfile behind a sha256 pin (pre-compiled-binaries + pattern, JOURNAL pattern #5) +- Logo: upstream wordmark (1080x291) resized/padded to 256x256 PNG via + ImageMagick in a throwaway `alpine:3.20` container (host stays clean) +- start.sh: seeds `/app/data/server.toml` (random default_token via + openssl + placeholder `[server.services.example]` on :5200), starts + the status-page sidecar, `exec rathole --server` (config hot-reloads + on save) + +**Validation**: +- `docker build --cgroup-parent ukrrs-batch.slice -t rathole-cloudron:test` + → green (sha256 gate + `rathole --version` inside the build) +- Runtime smoke: status page HTTP 200, control port :2333 reachable, + hot-reload detected an appended service within 3s +- **End-to-end tunnel test**: second container ran `rathole --client` + against the packaged server; `curl host:15200 → server:5200 → + client → client:9999` returned the client's page — token auth, + control channel, data plane, and lazy bind all proven + +**Files Created**: +- Dockerfile (pre-compiled binary, sha256-pinned, cloudron/base:4.0.0) +- CloudronManifest.json (manifestVersion 2, httpAuth proxy, tcpPorts: + CONTROL_PORT 2333 + SERVICE_PORT 5200 x100, localstorage) +- start.sh (config seed + status sidecar + exec) — committed executable +- status.html (auth-proxied landing/health page) +- README.md (auth story, ports, client quickstart) +- CHANGELOG.md +- .env.example (RUST_LOG knob) +- .dockerignore (excludes the whole cloned repo/ from context) +- logo.png (256x256) + +**Commit**: `feat: add Rathole Cloudron package (Infrastructure) [#650]` + +--- + ## Packaging Pattern: Download Pre-Compiled Binaries ### When to Use diff --git a/Package-Workspace/Infrastructure/rathole/.dockerignore b/Package-Workspace/Infrastructure/rathole/.dockerignore new file mode 100644 index 0000000..f606d5e --- /dev/null +++ b/Package-Workspace/Infrastructure/rathole/.dockerignore @@ -0,0 +1 @@ +repo diff --git a/Package-Workspace/Infrastructure/rathole/.env.example b/Package-Workspace/Infrastructure/rathole/.env.example new file mode 100644 index 0000000..890983a --- /dev/null +++ b/Package-Workspace/Infrastructure/rathole/.env.example @@ -0,0 +1,8 @@ +# Rathole Cloudron package — environment knobs +# +# Cloudron injects these at runtime (App -> Configure -> Environment); +# they are not secrets. + +# Log level for the rathole server: error | warn | info | debug | trace. +# Default when unset: info +RUST_LOG=info diff --git a/Package-Workspace/Infrastructure/rathole/CHANGELOG.md b/Package-Workspace/Infrastructure/rathole/CHANGELOG.md new file mode 100644 index 0000000..e82fde2 --- /dev/null +++ b/Package-Workspace/Infrastructure/rathole/CHANGELOG.md @@ -0,0 +1,27 @@ +# Changelog — Rathole Cloudron Package + +## 1.0.0 (2026-09-01) + +Initial Cloudron package for Rathole 0.5.0 (server mode). + +- Pre-compiled-binaries pattern: upstream `x86_64-unknown-linux-gnu` + release zip downloaded at build time behind a sha256 pin (musl release + builds were dropped upstream in v0.5.0). +- Runtime `cloudron/base:4.0.0` (Ubuntu 22.04): the gnu binary requires + glibc >= 2.35, too new for the 3.x (20.04) base used by earlier + packages. +- `start.sh` seeds `/app/data/server.toml` on first run with a random + `default_token` (openssl) and a placeholder `[server.services.example]` + binding :5200 — upstream rejects a server config with zero services — + then execs `rathole --server`; the config hot-reloads on save. +- Verified end-to-end at build time: a client container tunneled its own + HTTP server through the packaged server (token auth, lazy service bind, + hot-reload of added services). +- TCP surface: control channel on 2333 + a 100-port tunnel exit range + (5200-5299) declared via `tcpPorts` with `containerPort`/`portCount`. +- No user concept → `httpAuth.type = proxy` gates a small static status + page served on the HTTP port (also serves as the platform health + check); tunnel access itself is controlled by rathole's mandatory + per-service tokens. +- Addons: `localstorage` only (config persistence; no database). +- Logo padded from the upstream wordmark to a 256x256 PNG. diff --git a/Package-Workspace/Infrastructure/rathole/CloudronManifest.json b/Package-Workspace/Infrastructure/rathole/CloudronManifest.json new file mode 100644 index 0000000..1f7f6f1 --- /dev/null +++ b/Package-Workspace/Infrastructure/rathole/CloudronManifest.json @@ -0,0 +1,43 @@ +{ + "manifestVersion": 2, + "type": "app", + "id": "io.cloudron.rathole", + "title": "Rathole", + "description": "Rathole is a secure, stable and high-performance reverse proxy for NAT traversal, written in Rust. This package runs the server side: clients behind NAT connect to the control port with per-service token authentication (optionally Noise/TLS encrypted) and expose their local services through tunnels on the reserved port range. Configuration lives in /app/data/server.toml and hot-reloads on save. No database required.", + "author": "rapiz1 / rathole-org", + "website": "https://github.com/rathole-org/rathole", + "documentationUrl": "https://github.com/rathole-org/rathole/blob/main/README.md", + "contactEmail": "cloudron@tsys.dev", + "tagline": "Secure, high-performance reverse-tunnel server for NAT traversal", + "version": "0.5.0", + "upstreamVersion": "0.5.0", + "healthCheckPath": "/", + "httpPort": 8000, + "httpAuth": { + "type": "proxy" + }, + "tcpPorts": { + "CONTROL_PORT": { + "title": "Control channel port", + "description": "TCP port that rathole clients connect to in order to establish tunnels. Change only if it collides with another exposed app on this Cloudron.", + "defaultValue": 2333, + "containerPort": 2333, + "portCount": 1 + }, + "SERVICE_PORT": { + "title": "Tunnel exit ports", + "description": "Start of the sequential port range reserved for tunneled services (100 ports by default: 5200-5299). Each [server.services.*] block in /app/data/server.toml binds one port from this range; the config hot-reloads, so services can be added without a restart.", + "defaultValue": 5200, + "containerPort": 5200, + "portCount": 100 + } + }, + "memoryLimit": 268435456, + "addons": { + "localstorage": {} + }, + "postInstallMessage": "Rathole is headless: there is nothing to click through. Open the **file manager** and edit **/app/data/server.toml** to define tunnel services (a random default token was generated on first start). The config hot-reloads on save. The client-side setup is documented in the package README.", + "mediaLinks": [], + "changelog": "Initial Cloudron package for Rathole 0.5.0 (server mode). Pre-compiled upstream binary with sha256 pin, token-authenticated tunnels on control port 2333 plus a 100-port service range (5200-5299), config with hot reload at /app/data/server.toml, and an auth-proxied status page on the HTTP port.", + "icon": "file://logo.png" +} diff --git a/Package-Workspace/Infrastructure/rathole/Dockerfile b/Package-Workspace/Infrastructure/rathole/Dockerfile new file mode 100644 index 0000000..0785731 --- /dev/null +++ b/Package-Workspace/Infrastructure/rathole/Dockerfile @@ -0,0 +1,48 @@ +# Rathole Cloudron Package +# +# Rathole is a secure, stable and high-performance reverse proxy for NAT +# traversal (frp/ngrok class, written in Rust). This package runs the +# SERVER side: the Cloudron box is the public-IP endpoint, rathole clients +# behind NAT dial the control port (2333) and expose their local services +# through token-authenticated tunnels on the 5200+ port range. +# +# Upstream: https://github.com/rathole-org/rathole (v0.5.0) +# - Ships a prebuilt x86_64-unknown-linux-gnu release binary (musl +# release builds were dropped upstream in v0.5.0) +# - No database, no user accounts; per-service tokens are mandatory +# +# Authentication: Rathole has NO user concept (tunnels are authorized by +# per-service tokens, optionally Noise/TLS encrypted at the transport +# layer). The status page on the HTTP port is gated by Cloudron's +# authentication proxy (httpAuth.type = proxy). +# +# Base image: cloudron/base:4.0.0 (Ubuntu 22.04). The upstream gnu binary +# is built on ubuntu-latest, so the runtime needs glibc >= 2.35 — +# cloudron/base:3.2.0 (20.04) is too old for it. +FROM cloudron/base:4.0.0 + +ARG RATHOLE_VERSION=0.5.0 +ARG RATHOLE_SHA256=3e7d0d0f365120cd3cd351d147d1a12ee960c8068b464d4dd533a3821873b80e + +# Pre-compiled-binaries pattern (see JOURNAL.md): download the pinned +# upstream release, verify the sha256 gate, install the binary. +RUN curl -fsSL -o /tmp/rathole.zip \ + "https://github.com/rathole-org/rathole/releases/download/v${RATHOLE_VERSION}/rathole-x86_64-unknown-linux-gnu.zip" \ + && echo "${RATHOLE_SHA256} /tmp/rathole.zip" | sha256sum -c - \ + && unzip -o /tmp/rathole.zip -d /usr/local/bin \ + && chmod +x /usr/local/bin/rathole \ + && rm -f /tmp/rathole.zip \ + && /usr/local/bin/rathole --version + +# Static status page served on the Cloudron HTTP port (platform health +# check + auth-proxied landing page). start.sh seeds /app/data/server.toml +# on first run and execs rathole in server mode. start.sh is made +# executable on the host, not at build time (Cloudron gotcha). +COPY status.html /app/code/status/index.html +COPY start.sh /app/start.sh + +WORKDIR /app/data + +EXPOSE 8000 2333 5200-5299 + +CMD ["/bin/bash", "/app/start.sh"] diff --git a/Package-Workspace/Infrastructure/rathole/README.md b/Package-Workspace/Infrastructure/rathole/README.md new file mode 100644 index 0000000..9196783 --- /dev/null +++ b/Package-Workspace/Infrastructure/rathole/README.md @@ -0,0 +1,95 @@ +# Rathole — Cloudron Package + +[Rathole](https://github.com/rathole-org/rathole) is a secure, stable and +high-performance reverse proxy for NAT traversal (frp/ngrok class, written +in Rust). This package runs the **server** side: the Cloudron box is the +public-IP endpoint, rathole clients behind NAT dial the control port, and +their local services are exposed through token-authenticated tunnels. + +## Packaging overview + +| Aspect | Choice | +|--------|--------| +| Pattern | Pre-compiled binaries (JOURNAL pattern #5) | +| Base image | `cloudron/base:4.0.0` (Ubuntu 22.04) | +| Upstream binary | `rathole-x86_64-unknown-linux-gnu.zip`, v0.5.0, sha256-pinned in Dockerfile | +| Addons | `localstorage` only (config persistence; no database) | +| Auth | No user concept → `httpAuth.type = proxy` (see below) | +| Runtime | `start.sh` seeds config, serves status page, execs rathole `--server` | + +Why pre-compiled: upstream ships release binaries and **dropped the musl +builds in v0.5.0** (only `x86_64-unknown-linux-gnu` remains for Linux +amd64). The gnu binary is built on ubuntu-latest, so the runtime needs +glibc >= 2.35 — hence `cloudron/base:4.0.0` (22.04) instead of the 3.x +series (20.04). Compiling from source would need a full Rust toolchain +build stage for no benefit. + +## Authentication (auth gate verdict: proxy) + +- Rathole has **no user concept**: no web UI, no accounts, no SSO hooks. +- Tunnels are authorized by **mandatory per-service tokens** (a random + `default_token` is generated into `/app/data/server.toml` on first + start). Transport can additionally be encrypted via Noise or TLS. +- The manifest declares `httpAuth: {"type": "proxy"}`: Cloudron's auth + proxy gates the HTTP status page, which is the only HTTP surface this + app has. The raw TCP tunnel ports cannot be HTTP-gated by design; the + token layer is their access control. + +## Ports + +| Port | Type | Purpose | +|------|------|---------| +| 8000 | `httpPort` | Status/health page (behind Cloudron auth proxy) | +| 2333 | `tcpPorts` | Control channel — rathole clients connect here | +| 5200–5299 | `tcpPorts` (100-port range) | Tunnel exit ports — one per `[server.services.*]` | + +With default install settings Cloudron bridges these 1:1 (external +5200-5299 → container 5200-5299). If the admin picks a different +`SERVICE_PORT` start value, the *container* ports stay 5200+ and only the +external numbering shifts — `server.toml` never needs editing for that. + +## Configuration + +`/app/data/server.toml`, seeded on first run, **hot-reloaded on save** +(add/remove services without restarting the app). The seed contains a +placeholder `[server.services.example]` on :5200 — rathole rejects a +server config with zero services, so keep at least one block. Example +service: + +```toml +[server.services.my_nas_ssh] +token = "" +bind_addr = "0.0.0.0:5200" +``` + +Client side (machine behind NAT): + +```toml +[client] +remote_addr = "your-cloudron.example.com:2333" + +[client.services.my_nas_ssh] +token = "" +local_addr = "127.0.0.1:22" +``` + +Then `ssh -p 5200 user@your-cloudron.example.com` reaches the NAS. + +Knob: `RUST_LOG` (error|warn|info|debug|trace, default `info`) via +Cloudron environment settings — see `.env.example`. + +## Build & test + +```bash +docker build --cgroup-parent ukrrs-batch.slice -t rathole-cloudron:test Package-Workspace/Infrastructure/rathole/ +docker run --rm --entrypoint /usr/local/bin/rathole rathole-cloudron:test --version +``` + +## Files + +- `Dockerfile` — pinned release download + sha256 gate on cloudron/base:4.0.0 +- `CloudronManifest.json` — manifestVersion 2, httpAuth proxy, tcpPorts (control + 100-port service range) +- `start.sh` — config seeding, status page, exec rathole server mode +- `status.html` — auth-proxied landing/health page +- `.env.example` — runtime knobs +- `logo.png` — 256x256, padded from upstream wordmark diff --git a/Package-Workspace/Infrastructure/rathole/logo.png b/Package-Workspace/Infrastructure/rathole/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c623d58425f51a9801983073aca2b1bc4fe77ed2 GIT binary patch literal 11463 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

L#%b>`hfH;G( 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 Hdj9_brl6<> literal 0 HcmV?d00001 diff --git a/Package-Workspace/Infrastructure/rathole/start.sh b/Package-Workspace/Infrastructure/rathole/start.sh new file mode 100755 index 0000000..08774cd --- /dev/null +++ b/Package-Workspace/Infrastructure/rathole/start.sh @@ -0,0 +1,54 @@ +#!/bin/bash +set -euo pipefail + +# Rathole runs in SERVER mode on Cloudron: the box has the public IP and +# rathole clients behind NAT dial in on the control port (container port +# 2333, bridged 1:1 by default). Tunneled services bind container ports +# 5200-5299, which Cloudron exposes in sequence from the SERVICE_PORT +# value chosen at install time. +# +# Rathole has no HTTP interface of its own, so a tiny static status page +# is served on the Cloudron HTTP port. That gives the platform a health +# check endpoint and gives the admin an auth-proxied landing page +# (httpAuth.type = proxy gates it at the platform edge). + +export RATHOLE_CONFIG="/app/data/server.toml" + +if [[ ! -f "${RATHOLE_CONFIG}" ]]; then + default_token="$(openssl rand -hex 24)" + cat > "${RATHOLE_CONFIG}" < + + + + +Rathole (server) + + + +

+

Rathole ▲ server running

+

Secure, high-performance reverse-tunnel server for NAT traversal

+ +

Rathole is headless — this page exists so Cloudron has a health + endpoint and you have a landing spot. Configuration is file-based and + hot-reloads on save: edit + /app/data/server.toml with the Cloudron file manager to add + or remove tunnel services. A random default_token was + generated on first start; copy it from that file.

+ + + + + + +
PortPurpose
2333Control channel — rathole clients connect here
5200–5299Tunnel exit ports — one per [server.services.*] block
HTTP (this page)Status/health only, gated by the Cloudron auth proxy
+ +

Client side (on the machine behind NAT), with a matching service block on the server:

+
[client]
+remote_addr = "your-cloudron.example.com:2333"
+
+[client.services.my_nas_ssh]
+token = "<same token as the server>"
+local_addr = "127.0.0.1:22"
+ +

Upstream docs: + github.com/rathole-org/rathole

+
+ + diff --git a/README.md b/README.md index c79a21c..d41f8d3 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ The Cloudron component focuses on packaging upstream free/libre/open application ### 📊 Current Progress - **Total Applications**: ~57 (see [GitUrlList.txt](GitUrlList.txt)) -- **Completed Packages**: 10/~57 (~18%) +- **Completed Packages**: 11/~57 (~19%) - **Packaging Templates**: Created ✅ -- **Packages Committed & Pushed**: 10 ✅ +- **Packages Committed & Pushed**: 11 ✅ - **Build Tickets**: 46 filed (#633-#678, umbrella [#632](https://projects.knownelement.com/issues/632), Redmine project 55); grist-core excluded (packaged upstream) @@ -32,6 +32,7 @@ The Cloudron component focuses on packaging upstream free/libre/open application | 8 | draw.io | Documentation-Tools | — | 8080 | none (auth proxy) | ✅ Committed | | 9 | Windmill | Automation | ~2GB | 8000 | localstorage, postgresql | ✅ Committed | | 10 | Easy-Gate | Infrastructure | 3.18GB | 8080 | localstorage (auth proxy) | ✅ Committed | +| 11 | Rathole | Infrastructure | 3.51GB | 8000, 2333, 5200-5299 | localstorage (auth proxy) | ✅ Committed | ### 📦 Packages in Development @@ -65,7 +66,7 @@ None currently in development. ### ⚡ Productivity Metrics -- **Packages Completed**: 10/~57 (~18%) +- **Packages Completed**: 11/~57 (~19%) - **Average Package Time**: ~30 minutes - **Success Rate**: 100% (all packages built successfully) - **Commits Pushed**: 100% (all packages pushed to remote) @@ -90,7 +91,7 @@ Applications are organized by function rather than programming language: | **Documentation-Tools** | Documentation and diagramming tools | 3 | 1/3 (33%) | | **Financial-Payments** | Payment processing and financial infrastructure | 1 | 0/1 (0%) | | **Financial-Trading** | Trading and financial algorithm platforms | 1 | 0/1 (0%) | -| **Infrastructure** | Infrastructure and networking tools | 6 | 1/6 (17%) | +| **Infrastructure** | Infrastructure and networking tools | 6 | 2/6 (33%) | | **Legal** | Legal and compliance applications | 1 | 0/1 (0%) | | **Low-Code** | Low-code and no-code platforms | 3 | 1/3 (33%) | | **Monitoring** | Monitoring and observability tools | 6 | 1/6 (17%) | @@ -127,7 +128,7 @@ Applications are organized by function rather than programming language: | [Fleet](https://github.com/fleetdm/fleet) | [GitHub](https://github.com/fleetdm/fleet) | Device management and monitoring | Monitoring | | [NetBox](https://github.com/netbox-community/netbox) | [GitHub](https://github.com/netbox-community/netbox) | IP address management (IPAM) and data center infrastructure management | Infrastructure | | [SeaTunnel](https://github.com/apache/seatunnel) | [GitHub](https://github.com/apache/seatunnel) | Data integration and streaming platform | Data-Management | -| [Rathole](https://github.com/rapiz1/rathole) | [GitHub](https://github.com/rapiz1/rathole) | Lightweight and high-performance reverse proxy | Infrastructure | +| [Rathole](https://github.com/rapiz1/rathole) | [GitHub](https://github.com/rapiz1/rathole) | Lightweight and high-performance reverse proxy | Infrastructure | ✅ Packaged | | [Easy-Gate](https://github.com/wiredlush/easy-gate) | [GitHub](https://github.com/wiredlush/easy-gate) | Gateway and proxy solution | Infrastructure | ✅ Packaged | | [Huginn](https://github.com/huginn/huginn) | [GitHub](https://github.com/huginn/huginn) | Agents that do things for you automatically | Automation | | [ConsulDemocracy](https://github.com/consuldemocracy/consuldemocracy) | [GitHub](https://github.com/consuldemocracy/consuldemocracy) | Open-source citizen participation platform | Collaboration | diff --git a/STATUS.md b/STATUS.md index 2e0d883..78eabfc 100644 --- a/STATUS.md +++ b/STATUS.md @@ -3,20 +3,20 @@ > **Human read-only. Agents maintain this file automatically after each work > session.** Do not edit by hand — the next agent run will overwrite it. > -> **Last updated:** 2026-09-01 by Crush (GLM-5.2) — Easy-Gate packaged -> (#651, Infrastructure, 10th package); auth gate verdict: no user concept, -> shipped behind the Cloudron auth proxy. +> **Last updated:** 2026-09-01 by Crush (GLM-5.2) — Rathole packaged +> (#650, Infrastructure, 11th package); auth gate verdict: no user concept, +> status page behind the Cloudron auth proxy, tunnels token-authenticated. ## Current State: STABLE (packaging phase, ongoing) -Cloudron packaging pipeline is operational. 10 of ~57 upstream applications are +Cloudron packaging pipeline is operational. 11 of ~57 upstream applications are packaged, committed, and pushed. Packaging templates exist for the core patterns. The gardening protocol (this file + AGENTS.md) keeps docs in sync. All remaining apps now carry build tickets (#633-#678) under umbrella [#632](https://projects.knownelement.com/issues/632) in Redmine project 55 — ready for the sequential grind-driver pattern. -## Completed Packages (10) +## Completed Packages (11) | # | Application | Category | Pattern | Port(s) | Addons | |---|-------------|----------|---------|---------|--------| @@ -30,6 +30,7 @@ ready for the sequential grind-driver pattern. | 8 | draw.io | Documentation-Tools | Official-image wrapper + auth proxy | 8080 | none (stateless) | | 9 | Windmill | Automation | Official-image wrapper + start.sh | 8000 | localstorage, postgresql | | 10 | Easy-Gate | Infrastructure | Multi-stage (Go) + auth proxy | 8080 | localstorage | +| 11 | Rathole | Infrastructure | Pre-compiled binaries + auth proxy | 8000, 2333, 5200-5299 | localstorage | Each package lives in `Package-Workspace///` and contains a `Dockerfile`, `CloudronManifest.json`, `README.md`, `CHANGELOG.md`, `logo.png`, @@ -118,7 +119,7 @@ Full write-ups of each pattern + challenges are in [`JOURNAL.md`](JOURNAL.md). | DevOps-Tools | 1 | 0/1 | | | Financial-Payments | 1 | 0/1 | | | Financial-Trading | 1 | 0/1 | | -| Infrastructure | 6 | 1/6 | easy-gate done | +| Infrastructure | 6 | 2/6 | easy-gate, rathole done | | Legal | 1 | 0/1 | | | Project-Management | 1 | 0/1 | | | Scientific-Computing | 2 | 0/2 | | @@ -132,7 +133,7 @@ Auth capability is a hard gate before packaging (see LDAP acceptable (risk flag), 🔄 = auth-proxy (no users), ❌ = local-only (unacceptable / blocked-on-auth). -### Completed packages (10) +### Completed packages (11) | App | OIDC | LDAP | Verdict | Note | |-----|------|------|---------|------| @@ -146,6 +147,7 @@ LDAP acceptable (risk flag), 🔄 = auth-proxy (no users), ❌ = local-only | draw.io | n/a | n/a | 🔄 proxy | **Packaged** with `httpAuth.type=proxy` (no users, stateless) | | Windmill | yes | no | ✅ preferred | **Packaged**; OIDC configured via Admin Settings UI (no env vars) | | Easy-Gate | n/a | n/a | 🔄 proxy | **Packaged** with `httpAuth.type=proxy` (no user concept; IP-subnet groups only) | +| Rathole | n/a | n/a | 🔄 proxy | **Packaged** with `httpAuth.type=proxy` on the status page; tunnels secured by mandatory per-service tokens (Noise/TLS optional) | ### Candidates researched