From 8d0b0f42f6a7c0bd6f4c77b344516ebc80abe8f8 Mon Sep 17 00:00:00 2001 From: reachableceo Date: Tue, 1 Sep 2026 18:34:29 -0500 Subject: [PATCH] feat: add Easy-Gate Cloudron package (Infrastructure) [#651] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Easy Gate 2.0.3 dashboard hub: multi-stage Go build (static binary on cloudron/base 3.2.0), config persisted at /app/data/easy-gate.json with real-time hot reload. No user model (IP-subnet groups only), so the app ships behind the Cloudron auth proxy (httpAuth.type=proxy); localstorage addon only, no database. Build validated green + smoke test HTTP 200. Docs synced (STATUS/README/JOURNAL at 10/~57). Ticket: https://projects.knownelement.com/issues/651 💘 Generated with Crush Assisted-by: Crush:glm-5.2 --- JOURNAL.md | 53 ++++++++++++- .../Infrastructure/easy-gate/.dockerignore | 4 + .../Infrastructure/easy-gate/CHANGELOG.md | 15 ++++ .../easy-gate/CloudronManifest.json | 25 ++++++ .../Infrastructure/easy-gate/Dockerfile | 38 +++++++++ .../Infrastructure/easy-gate/README.md | 75 ++++++++++++++++++ .../Infrastructure/easy-gate/logo.png | Bin 0 -> 32662 bytes .../Infrastructure/easy-gate/start.sh | 51 ++++++++++++ README.md | 11 +-- STATUS.md | 16 ++-- 10 files changed, 275 insertions(+), 13 deletions(-) create mode 100644 Package-Workspace/Infrastructure/easy-gate/.dockerignore create mode 100644 Package-Workspace/Infrastructure/easy-gate/CHANGELOG.md create mode 100644 Package-Workspace/Infrastructure/easy-gate/CloudronManifest.json create mode 100644 Package-Workspace/Infrastructure/easy-gate/Dockerfile create mode 100644 Package-Workspace/Infrastructure/easy-gate/README.md create mode 100644 Package-Workspace/Infrastructure/easy-gate/logo.png create mode 100755 Package-Workspace/Infrastructure/easy-gate/start.sh diff --git a/JOURNAL.md b/JOURNAL.md index c24e9a5..d744f13 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**: 7/~57 packages completed (~12%) +**Current Status**: 10/~57 packages completed (~18%) ## Completed Packages @@ -770,6 +770,57 @@ Dockerfile **Commit**: `feat: add Windmill Cloudron package (Automation)` +### 10. Easy-Gate (Infrastructure) ✅ +**Date**: 2026-09-01 +**Application**: Easy Gate — web dashboard hub for self-hosted infrastructure +(config-file driven, real-time hot reload, IP-subnet group visibility) +**Package Size**: 3.18GB (cloudron/base 3.2.0 dominates) +**Port**: 8080 +**Addons**: localstorage (auth proxy, no database) + +**Key Learnings**: +- **Auth gate verdict**: Easy Gate has NO user model at all — visibility is + purely IP-subnet based (groups with CIDR ranges), no login/OIDC/LDAP + anywhere in the codebase → packaged with `httpAuth.type = proxy` (the + draw.io pattern for user-less apps) +- Multi-stage Go build from the cloned repo (Webhook pattern): + `golang:1.23-alpine` builder, `CGO_ENABLED=0 -trimpath -ldflags="-w -s"` + (mirrors upstream Makefile), static binary onto `cloudron/base:3.2.0` +- Config-driven design: the app re-parses `easy-gate.json` every second — + no restart needed on config edits; start.sh only seeds the first copy +- `behind_proxy: true` matters on Cloudron: group subnet matching uses + `X-Forwarded-For` (Cloudron's nginx passes the real client IP) +- No `.env.example` shipped: the only knob is the JSON config file itself + (documented in the package README instead) +- Package-root `.dockerignore` (`repo/.git`, `repo/.github`, ...) keeps the + cloned repo's git history out of the build context (webhook shipped + without one and paid the context-size cost) + +**Build Process**: +- Upstream `wiredlush/easy-gate` v2.0.3 cloned into `repo/` (gitignored) +- `go mod download` cached before `COPY repo/ .` for layer reuse +- Logo converted from upstream `assets/logo.svg` to a 234x256 PNG via + ImageMagick on the host +- start.sh: seeds `/app/data/easy-gate.json` on first run, then + `exec /usr/local/bin/easy-gate` (env `EASY_GATE_CONFIG_PATH`) + +**Validation**: +- `docker build --cgroup-parent ukrrs-batch.slice -t easy-gate-cloudron:test` → green +- Throwaway smoke container: `GET /` → HTTP 200, seeded config verified + (`behind_proxy: true`), container removed after test + +**Files Created**: +- Dockerfile (multi-stage Go build) +- CloudronManifest.json (port 8080, localstorage only, httpAuth proxy, + healthCheckPath /, 256MB memory) +- start.sh (first-run config seed) — committed executable +- README.md (auth story, config table, usage) +- CHANGELOG.md +- .dockerignore (build-context hygiene) +- logo.png (upstream logo.svg → PNG) + +**Commit**: `feat: add Easy-Gate Cloudron package (Infrastructure) [#651]` + --- ## Packaging Pattern: Download Pre-Compiled Binaries diff --git a/Package-Workspace/Infrastructure/easy-gate/.dockerignore b/Package-Workspace/Infrastructure/easy-gate/.dockerignore new file mode 100644 index 0000000..4bce12f --- /dev/null +++ b/Package-Workspace/Infrastructure/easy-gate/.dockerignore @@ -0,0 +1,4 @@ +repo/.git +repo/.github +repo/build +repo/dist diff --git a/Package-Workspace/Infrastructure/easy-gate/CHANGELOG.md b/Package-Workspace/Infrastructure/easy-gate/CHANGELOG.md new file mode 100644 index 0000000..e4c4fa1 --- /dev/null +++ b/Package-Workspace/Infrastructure/easy-gate/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog — Easy Gate Cloudron Package + +## 1.0.0 (2026-09-01) + +Initial Cloudron package for Easy Gate 2.0.3. + +- Multi-stage build: Go 1.23 builder (`CGO_ENABLED=0`, upstream Makefile + flags) producing a static binary on `cloudron/base:3.2.0`. +- `start.sh` seeds `/app/data/easy-gate.json` on first run with + `behind_proxy: true` (Cloudron nginx passes the real client IP via + `X-Forwarded-For` for group subnet matching), then execs the binary. +- `httpAuth.type = proxy`: Easy Gate has no user model, so Cloudron's + authentication proxy gates access. +- Addons: `localstorage` only (config persistence; no database). +- Logo converted from upstream `assets/logo.svg` to a 256px PNG. diff --git a/Package-Workspace/Infrastructure/easy-gate/CloudronManifest.json b/Package-Workspace/Infrastructure/easy-gate/CloudronManifest.json new file mode 100644 index 0000000..40f3073 --- /dev/null +++ b/Package-Workspace/Infrastructure/easy-gate/CloudronManifest.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "manifestVersion": 2, + "type": "app", + "id": "io.cloudron.easygate", + "title": "Easy Gate", + "description": "Easy Gate is a web application that serves as the central hub for your self-hosted infrastructure. Services and notes are parsed in real time from a JSON/YAML config file (hot reload, no restarts), and items can be assigned to user groups that only see them based on their IP subnet. No database required.", + "author": "wiredlush", + "website": "https://github.com/wiredlush/easy-gate", + "contactEmail": "cloudron@tsys.dev", + "tagline": "A gate to your self-hosted infrastructure", + "version": "2.0.3", + "healthCheckPath": "/", + "httpPort": 8080, + "httpAuth": { + "type": "proxy" + }, + "memoryLimit": 256, + "addons": { + "localstorage": true + }, + "mediaLinks": [], + "changelog": "Initial Cloudron package for Easy Gate 2.0.3. Config-driven dashboard hub with no user model, fronted by the Cloudron authentication proxy; configuration persists at /app/data/easy-gate.json with real-time hot reload.", + "icon": "file://logo.png" +} diff --git a/Package-Workspace/Infrastructure/easy-gate/Dockerfile b/Package-Workspace/Infrastructure/easy-gate/Dockerfile new file mode 100644 index 0000000..94fc5bf --- /dev/null +++ b/Package-Workspace/Infrastructure/easy-gate/Dockerfile @@ -0,0 +1,38 @@ +# Easy Gate Cloudron Package +# +# Easy Gate is a web dashboard that acts as the central hub for self-hosted +# infrastructure: services and notes are parsed in real time (hot reload, +# once per second) from a JSON/YAML config file, and items can be assigned +# to user groups which only see them based on their IP subnet. +# +# Upstream: https://github.com/wiredlush/easy-gate (v2.0.3, Go 1.23, Fiber) +# - Single static binary (CGO_ENABLED=0), listens on 0.0.0.0:8080 +# - No database, no user accounts, no migrations +# +# Authentication: Easy Gate has NO user concept (IP-subnet grouping only). +# Packaged behind Cloudron's authentication proxy (httpAuth.type = proxy). +FROM golang:1.23-alpine AS builder + +WORKDIR /easy-gate + +# Cache dependency downloads separately from source changes. +COPY repo/go.mod repo/go.sum ./ +RUN go mod download + +# Build the static binary (mirrors upstream Makefile flags). +COPY repo/ . +RUN CGO_ENABLED=0 go build -trimpath -ldflags="-w -s" -o build/easy-gate cmd/easy-gate/main.go + +FROM cloudron/base:3.2.0 + +COPY --from=builder /easy-gate/build/easy-gate /usr/local/bin/easy-gate + +# start.sh seeds /app/data/easy-gate.json on first run (behind_proxy: true) +# and execs the binary. Made executable on the host, not at build time. +COPY start.sh /app/start.sh + +WORKDIR /app/data + +EXPOSE 8080 + +CMD ["/bin/bash", "/app/start.sh"] diff --git a/Package-Workspace/Infrastructure/easy-gate/README.md b/Package-Workspace/Infrastructure/easy-gate/README.md new file mode 100644 index 0000000..9485727 --- /dev/null +++ b/Package-Workspace/Infrastructure/easy-gate/README.md @@ -0,0 +1,75 @@ +# Easy Gate Cloudron Package + +## Description + +Easy Gate is a simple web application that serves as the central hub for +your self-hosted infrastructure. Services and notes are parsed in real time +from a JSON/YAML configuration file — Easy Gate re-reads it every second, so +changes apply immediately without restarts. Services and notes can be +organized into categories and assigned to user groups, so items are shown +only to users matching specific IP subnets (e.g. LAN vs VPN). + +This package builds the upstream Go source (v2.0.3) as a static binary and +runs it on the Cloudron base image. + +## Authentication + +Easy Gate has **no built-in user model** — visibility is controlled by +IP-subnet groups, not accounts. This package therefore uses **Cloudron's +authentication proxy** (`httpAuth.type = proxy` in `CloudronManifest.json`). +The Cloudron admin restricts which platform users/groups may reach the app; +authorized users must present Cloudron credentials before the dashboard +loads. This is the recommended pattern for utility apps without their own +user directory. + +Because Cloudron fronts the app with its reverse proxy, the seeded config +sets `behind_proxy: true` so group matching uses the real client IP from +`X-Forwarded-For`. + +## Features + +- Real-time config parsing (JSON or YAML) with no application restarts +- Group-based visibility of services and notes by client IP subnet +- Service categories, themes, and custom CSS support +- Standalone static Go binary with no external dependencies +- No database required + +## Configuration + +### Ports +- **8080**: Easy Gate HTTP (the only exposed port) + +### Config file +All configuration lives in **`/app/data/easy-gate.json`** (created with a +sane default on first start). Edit it with the Cloudron file manager; +changes are picked up within a second. Key options: + +| Option | Purpose | +|--------|---------| +| `title` | Dashboard title | +| `theme.background` / `theme.foreground` | Colors; or `theme.custom_css` for a custom stylesheet | +| `groups[].name` / `groups[].subnet` | Groups defined by client IP subnet (CIDR) | +| `services[]` | Entries: `name`, `url`, `category`, `groups`, `icon` | +| `notes[]` | Entries: `name`, `text`, `groups` | +| `behind_proxy` | Keep `true` on Cloudron (real client IP via X-Forwarded-For) | + +Items with an empty `groups` list are visible to everyone (whoever passes +the Cloudron auth proxy). + +### Storage +- **`/app/data`** (localstorage addon) holds the config file. Nothing else + is persisted; no database addon is used. + +## Usage + +1. Install the package on Cloudron. +2. In the Cloudron access control, grant the desired users/groups access. +3. Open the app location — you'll be prompted for Cloudron credentials. +4. Edit `/app/data/easy-gate.json` to list your services and notes; the + dashboard updates within a second. + +## Upstream + +- **Repository**: https://github.com/wiredlush/easy-gate +- **Version packaged**: 2.0.3 (Go 1.23, Fiber) +- **License**: MIT diff --git a/Package-Workspace/Infrastructure/easy-gate/logo.png b/Package-Workspace/Infrastructure/easy-gate/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3ee2e453027fc33a0c76d37fb97cc712a6f09a02 GIT binary patch literal 32662 zcmXV11w7pU|2H*3HhoM^Gj(FxobHXu!!R`*(>t{>-EDHZYwD}p;l%&r z_xO9b$9bst=l%Y?;u)_`q^7zO(PQezSXfv@P-S^-;C&kl>j4%%F7Q>8 zM&PP!2*<)A?zn$F=y55s0=|jo4pmdY+a`EGNh8q45y6Rt6(|Xnf34#^yL$sIp_{7N zh`gCxlweYa`<*vUN90he=%5cwPl9-}G7VpSC#1oZJn8++zf8L}9;_7%*WA(~dMxzI zVj73Uv9&Bxi-S_LH|6qpqQz&MGoDEy(sIdUv42ivGb5r2ef*q>c(|!Ck0YJ}dgR~! z)CK-c^us?1hl8r-Gaicw%179#e}E6YVvlxkr9h}F+}e75E-5L=`Z>0^>s)De-02%R zNiWyoE37bY4l8?mt`9?1viQu47y{7DR+Ee+BJD9FLSpmV>gp zLnl;zh4?4iU!7Fa!^bHfw`^}_!!AHzva`#uM+G`(KOq|i2`oJy^(_$+I#oYVJCn19 z4tqq)6)1m~H*O#{TZ;<3#K$g=-GleAS6k7oYGB1F%Exj!(lTFIpacK?_udlmCo1QC z7r00F>Q7jMR143o*YNsBWJF zwe)=l*W+32y`nO((++%A!fE0+SsoM-ZmvAOiD!wh!}beih9)%Tpvoh91I=B|Fj7qX zrt!ZU6MMn`JplcAg=T>s6~I>tl+j_mS5=+sDYLnI=`+av;{mg07B<6ork65c3%lm1 z-%nb7mS_3erF*{g1Vu0gGUZL!r+GFwMdkF0XR6N`H3;-qOqEt0MD;>-r5@~Jk%2x0 z|7aF|rJEZe{~YU4FKueS`LIMv0MaUV6DICv%WB8F8JM&4Y*xA~<}=c+XiA;sPUX)q z_UrGD zfvqT?SAVyh0s^Vl$@Zbjh55?1y>OQnXpScq3MwTh4*RzZ#zOb-JfD>YdG_+&w%nfd zGSs`mQ(7Ic@$womblK>V;W5&uVW2bH&hhcD1`gI17T-a#U=~Twb0y zx|h54C91KF?O2LThvE0&S8S`-o%3YBQer+VDeaz>rOdrvaA&53my$ zE8-LAqjK4Dog(X%=0@dDb3t7zG2bK2OqdAOV318SUHFdb39gBOxM1~glFK_~w>42X z`V=xiM=?kp^Wd6i?%1+#h4!C(_ZE>C2oae=)Vq|d{sy09qTSf9!|#Uqy{i4jA4pK} z^X&YZ3T`-CB_hBGz`$Brk`I5{eG{JBJ5R0hY(NKkA&!C}8>v<2p%JtXh$DLC0}8Xq zUNg`cNhxKuqO|nn1O?2?^L|LVWHwAkpDXg#+sU0c&&BZ0IxcM=k01DP@7?(0KjHc>*kZ!@(Bv0mnaEBT)Ng71b_co2G9kI+!YOzz=4bMyVUe#+bJzN#({= z=841$w#_(?Enj-W-{v%|nTW3P53xIWjqOWTw%+RdnWR!AFJVzXru+QT_1$-~VY+{u zmS8Og23+L~IMJrb99ZDdZpCEtzYM=IBTRAxZGOpTVdPocq?U(3jpbKod>hjV)5=NY zAV0b)?@v;y)tDvClSr2mYC7W`mT+;egchFK8_>WF&KHME9~o6n)fZuT0gu7Sw)6~C zeAWmtN5o~T5oQXSaP5@95@d()!4u_RO8m+q6o=N)z%|Bm0fDsRMeX0rKe{EQjx~tE zBPV(r^Kgn5l(nzc0KZ!gLc(v*eKq=oj^yuE4cjwQ_$B6fX^4(O`6Scgf|-?KPg_K~ zr@=7nJRhWpzP^49+PCSZ^#g1`VsdsW6CNG|ktm+sFc0-{9z5b5SI+;KKe z_ut8oV18Nmm$B}5*-Xjc*je8D(75%8(49sr_(j= z3ZJi=(BHubRWB9liZ8nOUXa+>0o?;eV>c*RrAHtll>^v)>LYV{FV1lkIP zjRenv1Lpi@a;IxP)zffpO}ub2u}6dz^Cv8hE;UKD9Gcw~evG_JwAf<$`J7K~2J5d@ zA4w6TAp_3v=EMo&!^NRR2Haho4g|_Y>FJh6{6;_;bkB-VNbLy{OGJ+>euE`~d7hjd zT=XoF_c@9r`I|viFjKIhenNv{0yi|lKgnu_>Y&0!f47&&OmLE#oAGadR5nb(l;YQv zL^WbGLTOh^H#@mCY|=XW_i%h=fuAsiRWyyTlRWgH8xDSOZXXmaI()^AN#{e zfJZ{VcT0KKb;onJk$(5a)@CD^^CP5>@W}1#fK&#zgB8I12@PoXm z9jF#X^@M$lu>(m|uR|*ab>)e_@(SOACck<}D{wlkK6hP#Wh3N!x@8szsX|0B-k8>W z&X@T&32W&k@fpy>-A4G{fo|cmjZK(=DH($$Hxrp;il8g~^|yZlrDPkyBW@ZE*Cl3$ z@MyD9-Lu4BL3e>z@_G5F#6`>=W2jWCG)PPndTsiMxC_j#BUCJZ9B^HMfAxHxTwSMJ z{L`SqZWLrAtjf)>(?m;1Uw)nkRzbCCBrw=LG;`B)o6=wGLWf`ox(n(K5+h1^7zHuc zXU)ElUrD#U!Ok*n{QW17PQvr>4|~?(ki>!O&6UTuK^uNCvAQOvHRD!^R*3Gx4;1-V zkl;E}GZ3E?pP=&yY=^2~7~uTr6+@tp7=bfoytNtT9k{Cz4=R`;UyA+>o-ykT^V{mGH~+_Ij@bJoc35BP*`+NGQ}WXf+xawZ}&t-L@R#?hm?AJaHWT*yUV z{!G_icZWVF))3@oE?l1S-~JR})&7`$ju2#cg>bdPSb6G_q9I{Z7uMdS~C)oTPU()s#Q!(&6}?Q{*;_4}HMVFQ)d{ zV>yA?{jgEzqU%5_k!58<-)w!SyoDYV&&=dzB5#Tp+YR=@nfvI}`fxaY;DGWW&aVgc z%-vts>vkUn(A4JEa_3xjb)LQa-80bD;WD(;gfU_t3d=lP$_&1we;=of^sc(XHkCc< zYG0+D4T3rT*ao^fdHj?@o;n3xDWaXM?`FGF`qYeLf38On)QBqi zRQLFg4{UzQaJ#wM@IeLn!X~w2;wJ7O{PG#p&qCK|XEDwrG3gJJdo?E3>K8t@3i`LUNfCV|bjHmq+|3Nr z-~OaYsg%_hnjpv_W#e3j1be zoytrA4o)cevF9!hMQ#qh==UtNyYgM%@ItxiO)G9~q-g)SHeH^&Rt;O6>ru&@?|Al# zxB_d7HNM+tliE!9Tdu7d+~0SE&4K&-MA9XQ_k{U-1kYKw()FXRAnX_Rc6qP!eSMGe zBYFQRq`!Id1_0jY*nZq1EjUbWMQ+D_pGVag119mrJL9?n$hu5AhdwpfA?hpLlYqZklsq55;KFJKU#EILgF4#7^1e=iG?kWz4E+s5Av+M zxDVm$XDI8|7J1n>GvgA|BucJ7Icady{EB}$TNY*AzO|n{*ZCyT+ZQPthgCLVF9g6} zFyB+z=?A-≶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 literal 0 HcmV?d00001 diff --git a/Package-Workspace/Infrastructure/easy-gate/start.sh b/Package-Workspace/Infrastructure/easy-gate/start.sh new file mode 100755 index 0000000..6fbf73f --- /dev/null +++ b/Package-Workspace/Infrastructure/easy-gate/start.sh @@ -0,0 +1,51 @@ +#!/bin/bash +set -euo pipefail + +# Easy Gate is entirely config-file driven. The config lives in /app/data so +# it survives restarts and updates; seed a Cloudron-friendly default on first +# run. "behind_proxy" is true because Cloudron fronts the app with its nginx: +# X-Forwarded-For carries the real client IP, which the group subnet matching +# relies on. + +export EASY_GATE_CONFIG_PATH="/app/data/easy-gate.json" + +if [[ ! -f "${EASY_GATE_CONFIG_PATH}" ]]; then + cat > "${EASY_GATE_CONFIG_PATH}" <<'EOF' +{ + "addr": "0.0.0.0:8080", + "use_tls": false, + "cert_file": "", + "key_file": "", + "behind_proxy": true, + "title": "Easy Gate", + "theme": { + "background": "#FFFFFF", + "foreground": "#000000" + }, + "groups": [ + { + "name": "lan", + "subnet": "192.168.0.0/16" + } + ], + "services": [ + { + "name": "Cloudron", + "category": "Platform", + "url": "https://my.example.com", + "groups": [] + } + ], + "notes": [ + { + "name": "How to configure", + "text": "Edit /app/data/easy-gate.json with the Cloudron file manager. Easy Gate re-parses the config every second: changes apply immediately, no restart needed.", + "groups": [] + } + ] +} +EOF + echo "Seeded default config at ${EASY_GATE_CONFIG_PATH}" +fi + +exec /usr/local/bin/easy-gate diff --git a/README.md b/README.md index 554a39b..c79a21c 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**: 9/~57 (~16%) +- **Completed Packages**: 10/~57 (~18%) - **Packaging Templates**: Created ✅ -- **Packages Committed & Pushed**: 9 ✅ +- **Packages Committed & Pushed**: 10 ✅ - **Build Tickets**: 46 filed (#633-#678, umbrella [#632](https://projects.knownelement.com/issues/632), Redmine project 55); grist-core excluded (packaged upstream) @@ -31,6 +31,7 @@ The Cloudron component focuses on packaging upstream free/libre/open application | 7 | Corteza | Low-Code | 436MB | 80 | localstorage, postgresql | ✅ Committed | | 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 | ### 📦 Packages in Development @@ -64,7 +65,7 @@ None currently in development. ### ⚡ Productivity Metrics -- **Packages Completed**: 9/~57 (~16%) +- **Packages Completed**: 10/~57 (~18%) - **Average Package Time**: ~30 minutes - **Success Rate**: 100% (all packages built successfully) - **Commits Pushed**: 100% (all packages pushed to remote) @@ -89,7 +90,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 | 0/6 (0%) | +| **Infrastructure** | Infrastructure and networking tools | 6 | 1/6 (17%) | | **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: | [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 | -| [Easy-Gate](https://github.com/wiredlush/easy-gate) | [GitHub](https://github.com/wiredlush/easy-gate) | Gateway and proxy solution | Infrastructure | +| [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 | | [BOINC](https://github.com/BOINC/boinc) | [GitHub](https://github.com/BOINC/boinc) | Open-source software for volunteer computing | Scientific-Computing | diff --git a/STATUS.md b/STATUS.md index 37aecaa..2e0d883 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) — 46 build tickets filed -> (#633-#678, umbrella #632, Redmine project 55); clone-repos.sh repaired -> and re-run; Cloudron redis-addon correction (NetBox re-rated viable). +> **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. ## Current State: STABLE (packaging phase, ongoing) -Cloudron packaging pipeline is operational. 9 of ~57 upstream applications are +Cloudron packaging pipeline is operational. 10 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 (9) +## Completed Packages (10) | # | Application | Category | Pattern | Port(s) | Addons | |---|-------------|----------|---------|---------|--------| @@ -29,6 +29,7 @@ ready for the sequential grind-driver pattern. | 7 | Corteza | Low-Code | Pre-compiled binaries | 80 | localstorage, postgresql | | 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 | Each package lives in `Package-Workspace///` and contains a `Dockerfile`, `CloudronManifest.json`, `README.md`, `CHANGELOG.md`, `logo.png`, @@ -117,7 +118,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 | 0/6 | | +| Infrastructure | 6 | 1/6 | easy-gate done | | Legal | 1 | 0/1 | | | Project-Management | 1 | 0/1 | | | Scientific-Computing | 2 | 0/2 | | @@ -131,7 +132,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 (9) +### Completed packages (10) | App | OIDC | LDAP | Verdict | Note | |-----|------|------|---------|------| @@ -144,6 +145,7 @@ LDAP acceptable (risk flag), 🔄 = auth-proxy (no users), ❌ = local-only | Corteza | yes | no | ✅ preferred | Native OIDC via `auth.external.providers.openid-connect.*` | | 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) | ### Candidates researched