Ship local distribution and publisher

Task 10132 · Done · Coder · created · updated

## Original requirement (verbatim, epic body) > Build new deployment type for Jaah: local. Do a web research and copy the model of OpenClaw, N8N and other similar products: > - Open source in a public repo (for not private), mirrored some of the components from main repo > - A package of node + CP that user runs on his machine, no multiple machines, single user, no advanced functionality, no networking, no nodes > - Should be a very easy start - one line install and/or build from repo and/or docker compose > - Limited number of accounts > > Analyze and propose options Settled upstream (do not reopen): PRD (Task 10080) decisions D1–D16 + assumptions S1–S9; Architecture (Task 10081) decisions D17–D29 — the **1+3 hybrid**: one codebase with a build-time edition, an allowlist manifest + tree builder + per-PR mirror-build gate, public repo `jaah-ai/jaah-local` (fair-code licence adapted from n8n's Sustainable Use License, read-only mirror: issues open, PRs closed; images on GHCR `ghcr.io/jaah-ai/jaah-local-{api,web,node}`), published on every `master` promotion (D19) and fail-closed until the licence is approved (D24). Node placement (D18): native by default on Linux; macOS/Windows run the node container. ## Scope of this child **C7a. Public distribution files + mirror publisher + staging repo (Arch §2.7, first half of the architecture's C7; the planner split C7 so each half is one reviewable PR).** Owns the distribution files, the publisher workflow, the staging repo/environment setup and the image-build gate step. A workflow can only be dispatched once it is on `develop`, so the first staging publish and the AC-1/AC-2/AC-3 staging oracles run in C7b, right after this child merges. ## Approach (from the settled architecture — realize it, do not redesign) - **`infra/local-edition/` (mirrored to the public repo root; add `infra/local-edition/**` to the manifest `include` with the `layout` mapping):** - `compose.yml`: `postgres:18` (plain), `api`, `web` (nginx serve stage per `Dockerfile.frontend`), `node` under profile `node`; images `ghcr.io/jaah-ai/jaah-local-{api,web,node}:${JAAH_VERSION}`, each with a `build:` block (AC-6); every publish on `127.0.0.1:<port>` — the web port, and the API on exactly the port named in C3's `app/local_node.py` (the native node's CP URL); volumes `jaah-secrets`, `jaah-node-identity`, `jaah-node-home`, DB and attachments; no MinIO. - `install.sh` (Linux/macOS) and `install.ps1` (Windows → Docker Desktop), one line from the README: check Docker → fetch `compose.yml` for the latest tag → `docker compose up -d` → on Linux run C5's `jaah-local-node install` (WSL counts as Windows: `WSL_DISTRO_NAME` set or `/proc/sys/fs/binfmt_misc/WSLInterop` exists → container node profile) → print `http://localhost:<port>`. No config edit. `--upgrade`: pull the new tag, `up -d`, and `jaah-local-node install --upgrade <tag>` when the native unit exists. - `README.md` (quick start ≤ 25 lines, no node/org/tailnet words, every command copyable, plus a build-from-source section), `LICENSE` (adapted n8n Sustainable Use License with the cap clause and the anti-hosting clause — DRAFT pending D24), `RELEASE-CHECKLIST.md` (manual macOS/Windows/codex/antigravity/pi oracles, the no-auth public one-line run, and every CLI host with no telemetry off-switch from C5's PR). - Public-repo CI under `infra/local-edition/.github/`: on tag `v*` build the three images from the repo and push to GHCR with `GITHUB_TOKEN`; a workflow auto-closing fork PRs with a pointer to Issues; actions pinned by SHA. - **Publisher** `.github/workflows/local-edition-mirror.yml` (modelled on `agent-sdk-mirror.yml`): - Triggers: `push` to `master` (D19) and `workflow_dispatch`. - Destination: read from the GitHub environment variable `LOCAL_EDITION_DEST` of the environment the job runs in; a `workflow_dispatch` input may select the environment. No hard-coded default destination anywhere. - Two environments: `local-edition-mirror` (master-only; holds the public deploy key; absent until the operator approves `LICENSE` — the job **fails closed** without it) and `local-edition-staging` (restricted to `develop`; `LOCAL_EDITION_DEST=jaah-ai/jaah-local-staging` and the staging deploy key). The master push uses `local-edition-mirror` only. - Steps: `build_tree` → `check_tree` → one snapshot commit onto the destination `main` with the sync identity → tag `v<VERSION>` (`tools/version.sh`). - **Gate:** add the three local image `docker build`s (from the built tree) to `local-edition-check.yml`. - **Staging:** create the private repos `jaah-ai/jaah-local-staging` (same layout; issues on) and `jaah-ai/jaah-local-e2e-fixture` (for C7b), the `local-edition-staging` environment, and a staging deploy key — never echo a key value. Do not publish yet: the publisher can be dispatched only once it is on `develop` (C7b's first step). Full documents: the epic's settled `prd` and `architecture` artifacts (Tasks 10080/10081). Credentials & reach: - AC-1/2 mechanics on the private staging repo; fixture repo creation — `me/users/roman@gmind.tech/github_admin` (jaah-ai org admin) — **verified** (`credential_get` ok) - AC-1/2 on the public `jaah-ai/jaah-local` — operator action: approve the licence, create the repo and deploy key — **deferred**: the D24 licence gate. Mechanics are proven on staging first. - AC-3, AC-17 grep, AC-8 migrations, C1–C6 unit and gate checks — nothing beyond the worktree (plus GitHub Actions for `local-edition-check.yml`) — **verified**: the workflow runs in this repo's existing CI Out of scope: - Do NOT create the public `jaah-ai/jaah-local` repo or its deploy key, and do NOT publish anything public — operator action after licence approval (D24). The master-push path must fail closed while the `local-edition-mirror` secret is absent. - Do NOT run the fresh-VM release oracles (C7b). Do NOT rework C1–C6 internals except to fix a defect the image build exposes (fix in place, name it in the PR body). - Do NOT add a new alembic revision. Do NOT add docs-portal pages (deferred). Never write a credential value into a workflow, file, log or PR body. PRD §7 (verbatim) — the local edition, and the work to build it, MUST NOT: - Change the behaviour, UI or data model of the cloud deployments (dev, `dev.jaah.ai`; prod, `app.jaah.ai`). Any edition switch must default to today's behaviour there. - Ship, in the local edition or in the public repo: Stripe or billing; SES or system mail; Slack or Telegram; headscale or tailnet; cloud node launch (`apps/api/app/cloud_node_launch.py`); node groups (`apps/api/app/node_group_reconcile.py`); Multi-Node Hosts (`apps/api/app/vnodes.py`); NodeLink (`apps/api/app/nodelinks.py`); MicroVM (`docs/policies/microvm-nodes.md`); Jarvis (`apps/site-widget/`); the support knowledge base (`apps/api/app/support_kb/`); project memory (`apps/api/app/project_memory/`); SSO, SCIM or multi-org tenancy; the mobile app (`apps/mobile/`); GitHub Actions self-hosted runners (`infra/host/gha-runners/`); `infra/jaah/` (Terraform) and `infra/host/`. - Require pgvector, Redis, S3/MinIO, or any cloud account. - Phone home: no telemetry, no version check, no licence or activation server. - Accept community PRs into the public repo, or require a CLA. - Offer data export from local to Jaah Cloud, or attaching the local node to a cloud org. - Rewrite the control plane for SQLite, or install the control plane natively without Docker. - Expose any port beyond loopback, or support more than one machine. - Make the caps (3 accounts, 10 concurrent sessions) configurable by any setting, env var, flag or UI. Done means: - Publisher tests: `actionlint` clean on both workflows; a guard step/test proves the job fails closed when the environment's deploy key or `LOCAL_EDITION_DEST` is absent, the destination comes only from `LOCAL_EDITION_DEST`, and the `master` push binds only `local-edition-mirror`. - `build_tree` output contains `LICENSE`, `README.md`, `compose.yml`, `install.sh`, `install.ps1`, `RELEASE-CHECKLIST.md` and `.github/` at the root (layout mapping) and `check_tree` is green on it. - `LICENSE` names both the limits clause (3 accounts / 10 concurrent agents) and the anti-hosting clause (AC-1: "The licence must explicitly forbid two things: (i) removing or circumventing the account and concurrency limits, and (ii) offering the software as a hosted or multi-tenant service. Fail if the licence is OSI-permissive, or if it does not name the limits."); README quick start ≤ 25 lines with no node/org/tailnet words (AC-7). - Staging repo, fixture repo and the `local-edition-staging` environment (develop-only) exist — cite `jaah-forge api get` output, no key values. - `local-edition-check.yml` image-build step green on this PR; `./dc check quick` green. - **AC-1, AC-2, AC-3 staging oracles: run in C7b** (they need this child merged first).

Outcome

Local-edition distribution (compose, installers, Dockerfiles, draft LICENSE, README, checklist, public CI) and a fail-closed mirror publisher. Staging repos/env set up; image gate green; PR #6948 merged 76c7b3cc, Jaah Deploy succeeded. Drafts 10156, 10157. Memory: proposed 1 gotcha.

Pull request

Artifacts

Activity

  • Draft
  • Ready
  • Runner started
  • Runner ended (done)