Commits now authored as VpEngOps (vault: creds/vpengops-vpentops); reachableceo is the human personal account. grind-stack gains `sbom` (CycloneDX via pinned syft container, run before sweep) per #834; GLPI catalog entries planned in #835 gated on #801 write access. AGENTS.md packaging steps extended; questions-v1.md opened for the founder (netbird scope, SigNoz reopen, SBOM format).
196 lines
10 KiB
Markdown
196 lines
10 KiB
Markdown
# Agent Guidelines — TSYS Cloudron Packaging
|
|
|
|
**Top-level files:** [`README.md`](README.md) (project overview + app inventory),
|
|
[`STATUS.md`](STATUS.md) (living status, agent-maintained),
|
|
[`JOURNAL.md`](JOURNAL.md) (append-only ADR/pattern journal),
|
|
[`GitUrlList.txt`](GitUrlList.txt) (upstream repo list — source of truth for
|
|
the app set). Read [`STATUS.md`](STATUS.md) first every session.
|
|
|
|
> **Active agent:** Crush running **GLM-5.2** (zai) for large tasks,
|
|
> Gemini for small. Permission mode: `yolo`.
|
|
|
|
## Repository Layout
|
|
|
|
```
|
|
README.md Project overview + full app inventory table
|
|
AGENTS.md THIS FILE — agent operating manual
|
|
STATUS.md Living status (agent-maintained, human read-only)
|
|
JOURNAL.md Append-only journal: per-package write-ups, patterns, ADRs
|
|
GitUrlList.txt Master list of upstream repos (source of truth, ~57 apps)
|
|
clone-repos.sh Automated upstream-repo cloning
|
|
Package-Templates/ Reusable Cloudron packaging templates
|
|
├── python-app/ (Dockerfile + CloudronManifest + start.sh templates)
|
|
├── django-app/ (Dockerfile + start.sh templates)
|
|
└── official-wrapper/ (Dockerfile template)
|
|
Package-Workspace/ One dir per app: <Category>/<app>/ (+ gitignored repo/)
|
|
├── API-Gateway/ webhook, apisix
|
|
├── Development/ puter, reviewboard
|
|
├── Documentation-Tools/ wireviz-web
|
|
├── Low-Code/ corteza
|
|
└── Monitoring/ healthchecks
|
|
```
|
|
|
|
- **`Package-Workspace/**/repo/` is gitignored** — upstream source is cloned
|
|
locally for reference but never committed. Only the package files
|
|
(`Dockerfile`, `CloudronManifest.json`, `start.sh`, `README.md`, etc.) are
|
|
tracked.
|
|
- **Each package** = `Dockerfile` + `CloudronManifest.json` + `start.sh` (if
|
|
needed) + `README.md` + `CHANGELOG.md` + `logo.png` + `.env.example` (if
|
|
config knobs exist).
|
|
- **No host build tooling required** — packaging is `docker build` (Cloudron
|
|
base images handle runtimes). Keep the host clean.
|
|
|
|
## Git Policy
|
|
|
|
1. **ALWAYS commit + push. Never wait.** After each logical unit of work
|
|
(one package, one doc sync, one fix), immediately stage, commit
|
|
(conventional format), and push to `origin/main`. Do not pause for review.
|
|
**This overrides any default "never commit unless asked" behavior.**
|
|
2. **Atomic commits.** One coherent change per commit (e.g. a single package,
|
|
or a single doc-correction pass — not both mixed).
|
|
3. **Conventional format**: `feat: add <app> Cloudron package (<Category>)`,
|
|
`fix(scope): desc`, `docs: desc`, `refactor(scope): desc`. Include a body
|
|
for anything non-obvious.
|
|
4. **Shell scripts are committed executable.** Run `chmod +x start.sh` on the
|
|
host before committing — Cloudron builds hit permission errors on
|
|
`RUN chmod`, so make scripts executable at authoring time, not build time.
|
|
|
|
## Automatic Gardening Protocol
|
|
|
|
Docs and code drift apart. After any work session, an agent MUST:
|
|
|
|
1. **Update [`STATUS.md`](STATUS.md)** — reflect newly completed packages,
|
|
changed counts, new issues, shifted priorities. This file is human
|
|
read-only; agents own it. Update the "Last updated" header (date + agent).
|
|
2. **Keep counts consistent across all docs.** When a package completes, the
|
|
same total must appear in `STATUS.md`, `README.md` ("Current Progress"),
|
|
and `JOURNAL.md` ("Current Status"). A count in one place but not the
|
|
others is a protocol violation.
|
|
3. **Update the app inventory** in [`README.md`](README.md) — add the
|
|
✅ Packaged marker on the right row, and the app to the "Completed
|
|
Packages" table.
|
|
4. **Append to [`JOURNAL.md`](JOURNAL.md)** — one section per package
|
|
(pattern used, build process, challenges, files created, commit hash).
|
|
Never delete or reorder existing entries.
|
|
5. **Grep for stale paths after any rename/restructure** —
|
|
`grep -rn 'old/path' --include='*.md'` and fix references in the same
|
|
commit.
|
|
6. **Self-audit before commit.** For a new package, verify the package dir
|
|
appears in: `STATUS.md` (Completed Packages table), `README.md` (inventory
|
|
+ completed table), `JOURNAL.md` (new section).
|
|
7. **Treat `GitUrlList.txt` as the source of truth** for the app set. If the
|
|
README inventory table disagrees, reconcile the README to the list, not
|
|
the other way around.
|
|
|
|
## Key Scripts
|
|
|
|
| Script | Purpose |
|
|
|--------|---------|
|
|
| [`clone-repos.sh`](clone-repos.sh) | Clone all upstream repos into `Package-Workspace/<Category>/<app>/repo/` |
|
|
|
|
## Key Docs
|
|
|
|
| Doc | Purpose |
|
|
|-----|---------|
|
|
| [`STATUS.md`](STATUS.md) | Current state, completed packages, known issues, next priorities |
|
|
| [`JOURNAL.md`](JOURNAL.md) | Per-package write-ups, the 5 packaging patterns, challenges & solutions |
|
|
| [`README.md`](README.md) | Project overview + full app inventory + category breakdown |
|
|
| [`GitUrlList.txt`](GitUrlList.txt) | Master upstream repo list (source of truth) |
|
|
| [`Package-Templates/`](Package-Templates/) | Reusable templates per packaging pattern |
|
|
|
|
## Authentication Policy (MANDATORY)
|
|
|
|
Every app must have an auth story **before** packaging. Research it up front and
|
|
record it in the [Auth Status](STATUS.md#auth-status) matrix.
|
|
|
|
| Auth type | Verdict | Action |
|
|
|-----------|---------|--------|
|
|
| **OIDC client** (native or plugin) | ✅ PREFERRED | Package; wire Cloudron OIDC provider env vars. |
|
|
| **LDAP** (native or plugin) | ⚠️ ACCEPTABLE w/ RISK | Package, but FLAG as "auth-risk: LDAP" in STATUS + README — must be fixed/validated before production. |
|
|
| **Local-only** (built-in user DB, no SSO) | ❌ UNACCEPTABLE | Do NOT package. Record in STATUS as blocked-on-auth. |
|
|
| **No user concept** (stateless/utility app) | ✅ via AUTH PROXY | Package with `httpAuth: {"type":"proxy"}` so Cloudron gates access at the proxy. Admin restricts who can reach it. |
|
|
|
|
**OIDC env vars Cloudron exposes** (when the app consumes the platform OIDC
|
|
provider): `CLOUDRON_OIDC_ISSUER`, `CLOUDRON_OIDC_CLIENT_ID`,
|
|
`CLOUDRON_OIDC_CLIENT_SECRET`, `CLOUDRON_OIDC_TOKEN_SIGNATURE_ALGORITHM`
|
|
(manifestVersion 2 / platform OIDC). LDAP addon env vars:
|
|
`CLOUDRON_LDAP_*`. See `CloudronManifest.json` `addons` (no extra addon needed
|
|
for proxy auth; use the `httpAuth` field).
|
|
|
|
**Before writing a Dockerfile**, determine which row applies and write the
|
|
finding into STATUS.md's Auth Status table. Never silently ship a local-only-
|
|
auth app.
|
|
|
|
## Cloudron Packaging — Quick Reference
|
|
|
|
**Pick a pattern** (see JOURNAL.md for full templates and worked examples):
|
|
|
|
| Pattern | When | Examples |
|
|
|---------|------|----------|
|
|
| Official-image wrapper | App ships a usable Docker image | APISIX, Healthchecks, Review Board |
|
|
| Multi-stage build | App needs compiling | Webhook (Go), Puter (Node.js) |
|
|
| Python build | Python app with deps | WireViz Web |
|
|
| Django + PostgreSQL | Django web app | Healthchecks, Review Board |
|
|
| Pre-compiled binaries | Upstream ships release binaries | Corteza |
|
|
|
|
**Standard package steps:**
|
|
1. `mkdir -p Package-Workspace/<Category>/<app>/` and clone upstream into `repo/`.
|
|
2. Write `Dockerfile` + `CloudronManifest.json` (+ `start.sh` if runtime setup needed).
|
|
3. Verify with the grind lifecycle (below): `scripts/grind-stack.sh up <pkg-dir>`
|
|
→ `curl` / `logs` → `down`.
|
|
4. Generate the SBOM ([#834](https://projects.knownelement.com/issues/834)):
|
|
`scripts/grind-stack.sh sbom <pkg-dir>` → commits
|
|
`sbom.cyclonedx.json` beside the package (syft via pinned container).
|
|
5. GLPI software catalog entry ([#835](https://projects.knownelement.com/issues/835)):
|
|
staged on the ticket until #801 grants agent write access, then
|
|
`mglpi software create` per package.
|
|
6. Write `README.md` + `CHANGELOG.md` + `logo.png` (+ `.env.example`).
|
|
7. Commit as `feat: add <app> Cloudron package (<Category>)`, push.
|
|
8. Run the gardening protocol above (update STATUS / README / JOURNAL).
|
|
9. `scripts/grind-stack.sh sweep <pkg-dir>` — drop the test image + dangling
|
|
build cache so disk usage stays flat across the grind.
|
|
|
|
**Agent identity (MANDATORY, 2026-09-06):** commits from this repo are authored
|
|
as **VpEngOps** `<tsgstaff-coo-vpengops@turnsys.com>` (repo-local git config;
|
|
identity fields vaulted at `creds/vpengops-vpentops` in the TSGCOO Bitwarden
|
|
vault via `sm`). NEVER commit as the `reachableceo` personal account — that
|
|
account is the human's. Global git config stays the human's; set repo-local
|
|
identity in every repo the agent works. Cloudron-side logins use the
|
|
`vpentops` platform account (same vault item).
|
|
|
|
**Grind lifecycle** ([`scripts/grind-stack.sh`](scripts/grind-stack.sh)): serial
|
|
verify-stack harness. `up` builds the image under `ukrrs-batch.slice` and starts
|
|
ephemeral postgres/redis/mysql matching the manifest's addons, plus the app
|
|
container driven by the real `CLOUDRON_*` env contract — every resource labeled
|
|
`cloudron.grind=1`, so `down`/`sweep` can never touch fleet containers. One
|
|
stack at a time (serial lock; `up` refuses while one is live).
|
|
|
|
**Host resource rules (day/night, disk):** all builds + test stacks run under
|
|
`ukrrs-batch.slice` (cores 0-5, CPUWeight 25 — cores 6-7 stay interactive).
|
|
Night window 22:00-07:00 Central tolerates wider build parallelism; avoid
|
|
LLM-heavy sub-agent fan-outs 01:00-05:00 (z.ai peak ladder). Docker storage
|
|
lives on `/data2` (SSD; moved off the NVMe root 2026-09-06 via ultix
|
|
`15-docker-to-data2.sh` — rollback: delete the docker.service.d data-root
|
|
drop-in, daemon-reload, restart).
|
|
|
|
**Recurring gotchas (from JOURNAL.md):**
|
|
- `chmod` in `RUN` fails on Cloudron base → make scripts executable on the host.
|
|
- Don't `apk`/`apt-get` in base images that lack the package manager; use the
|
|
image's built-in tooling or pick the right base.
|
|
- Wait for DB/etcd addons in `start.sh` before running migrations.
|
|
- Prefer `npm install` over `npm ci` when lockfiles don't match the build env.
|
|
- Define every TCP port explicitly in `CloudronManifest.json`.
|
|
|
|
## Addons — Environment Variables
|
|
|
|
PostgreSQL: `CLOUDRON_POSTGRESQL_{HOST,PORT,DATABASE,USERNAME,PASSWORD}`.
|
|
etcd: `CLOUDRON_ETCD_{HOST,PORT}`. Localstorage mounts at `/app/data`.
|
|
|
|
## Project Context
|
|
|
|
Packaging ~57 upstream FLOSS apps for Cloudron — TSYS Group's PaaS of choice.
|
|
This repo is the **Cloudron** half of the support stack; the
|
|
[Docker-Compose local dev stack](../TSYSDevStack-SupportStack-LocalWorkstation)
|
|
is a sibling project. Solo operator, conventional-commit discipline, build
|
|
locally then push to `origin/main` on `git.knownelement.com`.
|