Skip to content

Latest commit

 

History

History
159 lines (126 loc) · 9.4 KB

File metadata and controls

159 lines (126 loc) · 9.4 KB

Contributing

ChannelGate (formerly Claude Gateway for Slack) is a self-hosted daemon that runs coding agents inside a container per conversation. Contributions are welcome, and because the gateway executes model output on someone's machine, review is strict about isolation, secret handling, and cross-platform behaviour. Everything below is what a reviewer will actually check.

By contributing you accept the Contributor License Agreement — the Signed-off-by trailer on each commit is that acceptance (CLA Section 7). Participation is governed by the Code of Conduct.

Set up

Requirements: Node >= 22.13 (the floor is exact — node:sqlite is stable from 22.13 and the daemon refuses to boot below it), Git, and Linux — the daemon refuses to boot anywhere else.

git clone <your fork> && cd <checkout>
npm ci
cp .env.example .env      # full live setup also needs rootless Podman + provider API credentials
npm start                 # admin UI + health endpoint on http://localhost:4747

.env is gitignored and never committed. Runtime state (config, sessions, logs, the SQLite database) lives outside the repository under the gateway runtime root, not in the checkout. INSTALL.md covers a full working install; the tests below need neither Slack credentials nor a running daemon.

Run the checks

npm test                   # full node:test suite (test/*.test.js)
npm run test:coverage      # the same suite with the enforced coverage floors — what CI runs
npm run check:static       # syntax, undeclared identifiers, whitespace invariants
npm run secret-scan        # dependency-free credential scan over tracked files
npm run check:dco -- origin/beta..HEAD  # every development commit carries a sign-off trailer

npm run test:security-coverage enforces the extra coverage floors on the security-critical modules; run it when you touch authorization, confinement, secrets, or the admin API. A single file runs with node --test test/<name>.test.js.

Tests must leave no scratch directories behind. Create temp directories with tempDir(prefix) from test/helpers.js (never a bare mkdtempSync(path.join(os.tmpdir(), …))) — it registers them for an exit-handler cleanup, and npm test also runs scripts/test-scratch-sweep.mjs first to sweep anything a crashed run left in the system temp dir.

Every one of these must pass before you open a pull request. CI runs the suite on Node 22.13 and 24 on ubuntu-latest.

Branches and review

Use your own fork, branch and isolated worktree. Start from the latest upstream beta, make focused commits and open a PR targeting beta with validation results. You do not need access to the publisher's GitHub account, private QA database or production host. Maintainers handle final integration and release.

beta receives all completed development work. main is the stable update branch. A release PR from beta to main requires the full test and live acceptance evidence defined in AGENTS.md, followed by explicit user confirmation for that exact tested candidate. Beta work is not authorization to release; required checks that are blocked or unexecuted prevent promotion.

Keep FEATURES.md and TEST-PLAN.md consistent with behavior. Include exact fixtures, setup, prompts/actions and pass rules for applicable Claude and Codex acceptance. Report live cases you could not execute explicitly so maintainers can run them; a local unit test is not a live pass. Never resolve a shared document conflict by discarding another contributor's evidence.

Deployments that serve a checkout directly may require serialized landing using npm run with-landing-lock -- <command>. Follow that host's operator instructions; ordinary contributors should not deploy or push directly to the upstream main branch.

Commit rules

  • Sign off every commit: git commit -s. The Signed-off-by: Your Name <you@example.com> trailer is how you accept CLA.md; a real name and a reachable address are required, and pseudonymous sign-offs cannot be accepted. Already committed without it? `git rebase --signoff `. CI rejects the pull request otherwise (`scripts/check-dco.mjs`).
  • Conventional, imperative subject, present tense, no trailing period: fix: reject an unsigned commit range, not fixed stuff.
  • One change per commit. A refactor and a behaviour change are two commits.
  • Stage only your own files — git add <path>, never git add -A / git commit -a. If the tree holds edits you did not make, leave them unstaged.
  • Explain why in the body. The diff already says what.

What we accept

Open a pull request directly for:

  • Bug fixes with a regression test that fails before the fix.
  • Documentation corrections and clarifications.
  • Tests covering existing behaviour, especially the security-critical modules.
  • Platform adapters and engine runners that follow the registry pattern described in AGENTS.md: a chat surface is an adapter plus a connector in src/platforms/ with a complete capability descriptor; an engine is a runner plus an entry in src/engines/registry.js. Adding a platform === "slack" or engine === "codex" branch anywhere else is the thing those registries exist to prevent, and it will be sent back.

Ask first — open an issue or a discussion before writing code — for:

  • new license tiers, limits, or anything touching license-key enforcement;
  • anything under src/ee/, which is proprietary, source-visible code owned by MAKEITFUTURE S.R.L. and is not under the Sustainable Use License — its own terms are in src/ee/LICENSE-EE.md, and LICENSE.md §3.2/§4.5 make removing, disabling, or circumventing the key verification, the usage limits, or the usage reporting a licence violation. We are happy to take bug reports and security findings about it; we cannot take patches to it without a prior discussion and a signed CLA covering that directory. The same applies to the enforcement call site in src/gateway/run.js (licenseAdmission()) and to the license_usage schema;
  • licensing, CLA, or trademark text (LICENSE.md, CLA.md, TRADEMARK.md, docs/LICENSING-*);
  • new runtime dependencies — the project is deliberately close to dependency-free outside the Slack, MCP, and Composio SDKs;
  • schema changes (migrations are append-only; an existing migration is never edited);
  • anything that changes the security model.

Where design notes go

Put a short design note in the pull request description: the problem, the option you chose, the options you rejected, and how it is tested. That is the durable record for a reviewer. Larger design documents, roadmaps, and audit notes are kept privately by the maintainers and are not part of this repository — do not add a docs/plans/ or a working-notes file to a pull request.

Behaviour changes update TEST-PLAN.md (the cumulative regression list) and, when they ship a user-visible capability, FEATURES.md and CHANGELOG.md.

Security issues

Do not open a public issue, discussion, or pull request for a suspected vulnerability. Report it privately per SECURITY.md — contact@makeitfuture.com, with the affected version, reproduction, and impact, and no live credentials or customer data. Rotate anything that may have leaked before you report.

The rules reviewers enforce

These are not style preferences; a change that breaks one of them does not merge.

  • Confinement is the product, and the container is the boundary. Every turn runs inside the channel's own container: a per-channel HOME volume, only the work folder (plus its clean workspace and artifact dir) mounted, no host home or gateway root by default. The explicit Full-access whole-home option widens this boundary and must remain off unless deliberately selected by the operator. Every channel folder still gets the lockdown settings — automatic memory off, curated permission allowlist, the MCP allowlist — as policy, never a sandbox block. An engine is never exec'd outside a container, and nothing a run can do changes what its container mounts. The boundary is filesystem and process isolation, not egress: Allow network is a per-channel switch the engines are told about, not a filter.
  • Secrets never ride a listing response. Listing endpoints return has* and last4 only. A value is fetched one at a time through the reveal endpoint, which re-checks the admin password and audit-logs what was revealed, never the value. New secret fields go in the reveal allowlist. Nothing is hardcoded and nothing is logged.
  • Linux only. ChannelGate targets Linux with systemd and rootless Podman: no macOS/launchd branches, no BSD-tool assumptions — and still no setsid or other platform-specific binaries; prefer portable Node APIs over shelling out and keep shell POSIX-portable.
  • A run is never killed for being quiet, and a waiting state always announces itself. Silence that looks like death is the bug the heartbeat and watchdog exist to prevent.
  • Platform capabilities are declared once and fail closed. Read them through the capability helpers; an undeclared capability resolves to the least capable value.
  • Only admins get skipped permissions, and authorization is checked before anything else runs.

Questions

Usage and troubleshooting: SUPPORT.md. Licensing, commercial use, and partner inquiries: contact@makeitfuture.com and docs/LICENSING-FAQ.md.