What you will learn
- The three ground rules that apply to every contribution
- How to set up the development environment
- Code organization by trust domain
- Coding standards, naming conventions, and style rules
- Testing requirements and security invariants
- The pull request and commit process
- Common gotchas that catch contributors
Ground rules
Three rules apply to every change:Security First
The six security invariants listed below are non-negotiable. No PR that weakens them will be merged.
Test Everything
Every change must pass the existing test suite, and new features must include tests.
Respect Trust Domains
Every line of code belongs to exactly one of the three trust domains. Never blur these boundaries.
Getting started
If any step fails, see the Dev Setup Guide for prerequisites (Anvil, Docker, environment variables).
Code organization
The repository is an npm-workspaces monorepo with packages organized by trust domain. The trust domain model is the single most important architectural concept in this project.Domain A — Untrusted
Adapters and plugins that run agent code. No keys, no direct RPC access, no signing.| Package | Description |
|---|---|
@clavion/adapter-openclaw | OpenClaw thin skill wrappers |
@clavion/adapter-mcp | MCP server for Claude Desktop, Cursor, IDEs |
@clavion/plugin-eliza | ElizaOS (ai16z) plugin with 5 actions |
@clavion/adapter-telegram | Telegram bot (agent + approval UI) |
Domain B — Trusted
The secure core. Keys, policy enforcement, signing, audit logging, RPC access.| Package | Description |
|---|---|
@clavion/core | API server, transaction builders, approval flow |
@clavion/signer | Encrypted keystore and signing |
@clavion/audit | Append-only audit trace (SQLite) |
@clavion/policy | Policy engine and config validation |
@clavion/preflight | Risk scoring and simulation |
@clavion/registry | Skill manifest validation and registry |
@clavion/types | Shared interfaces, schemas, RPC types |
Domain C — Limited Trust
Sandboxed execution. No key access, API-only communication with Core.| Package | Description |
|---|---|
@clavion/sandbox | Container isolation runner |
Tooling
| Package | Description |
|---|---|
@clavion/cli | Key management CLI (import, generate, list) |
@clavion/sdk | SDK interface (stub, planned for v0.2) |
tests/ (cross-package integration, security, and E2E tests), tools/ (fixture generation, hash utilities), examples/, docs/, docker/.
Coding standards
TypeScript
- Core Rules
- CJS/ESM Interop
- Strict mode is enforced (
strict: truein the root tsconfig, plusnoUncheckedIndexedAccess,noUnusedLocals,noUnusedParameters). - ESM with Node16 module resolution. All packages use
"type": "module"and theNode16module/moduleResolution settings. additionalProperties: falseon every JSON schema. No undocumented fields are allowed to pass validation.- viem is the preferred EVM library over ethers.
Naming conventions
| Element | Convention | Examples |
|---|---|---|
| Files | kebab-case | risk-scorer.ts, audit-trace-service.ts |
| Classes | PascalCase | PolicyEngine, WalletService |
| Interfaces / Types | PascalCase | TxIntent, PolicyConfig, RpcClient |
| Functions | camelCase | buildFromIntent, computeRiskScore |
| Constants | UPPER_SNAKE_CASE | MAX_SCORE, HIGH_SLIPPAGE_BPS |
Code style
- Prettier for formatting. Run
npm run format:checkbefore submitting. - ESLint for linting. Run
npm run lintbefore submitting. - Follow the formatting conventions already present in the codebase. When in doubt, let Prettier decide.
Testing requirements
All pull requests must satisfy:npm testpasses — this runs unit and integration tests.- New features include unit tests. If you add a builder, service, route, or adapter method, add corresponding tests.
- Fund-affecting features include security tests. Changes to signing, policy enforcement, approval flow, or key management must include tests that verify Domain B integrity.
- Mock RPC factories implement all
RpcClientmethods, includingreadNativeBalance. Incomplete mocks cause runtime failures in unrelated tests. - Test fixtures live in
tools/fixtures/. When adding a new valid fixture, also add its pre-computed hash tohash-fixtures.ts(the canonicalization test iterates all entries).
| Category | Command | Requirements |
|---|---|---|
| Unit | npm run test:unit | None |
| Integration | npm run test:integration | None |
| Security | npm run test:security | Docker (for sandbox tests) |
| E2E | npm run test:e2e | Anvil + BASE_RPC_URL |
Tests that require Docker or Anvil skip gracefully when those dependencies are unavailable.
Security rules (non-negotiable)
These six invariants are the foundation of the project’s security model. Every contributor must understand and uphold them.- Private keys exist only in Domain B — never in Domain A (skills/adapters) or Domain C (sandbox).
- Every signature passes PolicyEngine + Preflight — there are no bypass paths.
- Skills have no direct RPC access — only ISCL Core contacts the blockchain.
- All fund-affecting operations use TxIntent v1 — no arbitrary calldata signing.
- All critical steps are audit logged — correlated by
intentId. - Approval tokens are single-use with TTL — no replay.
Additional design rules
- New crypto logic goes in Domain B only, inside the appropriate package.
- New skill-facing functionality must be exposed via the ISCL API, never through direct module access.
- New external network calls must go through the RPC allowlist in Domain B.
- Sandbox code belongs to Domain C: no key access, no unrestricted network.
- Cross-domain communication always goes through the ISCL Core API (localhost HTTP).
Pull request process
Run the full test suite
npm run test:security.Submit a pull request
Include a clear description of what changed and why:
- A summary of the change (what problem it solves or what feature it adds).
- Which trust domain(s) the change touches.
- How it was tested.
PR review
Security-sensitive changes (Domain B, key management, policy, approval) require extra scrutiny and may take longer to review.
Commit style
The project follows Conventional Commits. Use the appropriate prefix for each commit.| Prefix | Purpose | Example |
|---|---|---|
feat: | New feature | feat: add swap_exact_out support for 1inch |
fix: | Bug fix | fix: prevent approval token replay across intents |
chore: | Maintenance tasks | chore: remove old doc/ directory |
build: | Build system changes | build: move Docker files to docker/ |
docs: | Documentation only | docs: add community files and restructure documentation |
refactor: | Code restructuring (no behavior change) | refactor: extract @clavion/types package |
test: | Test additions or changes | test: add Domain B integrity tests for replay protection |
Common gotchas
These are recurring pitfalls that have caught contributors before. Save yourself debugging time by reading them.JSON Schema validates structure only
JSON Schema validates structure only
Business logic like deadline expiration must be enforced in code, not in the schema. The schema validates types, patterns, and required fields — it does not enforce runtime constraints.
TxIntentSchema $defs/$ref with AJV
TxIntentSchema $defs/$ref with AJV
When embedding the schema in a wrapper object, hoist
$defs to the wrapper root. AJV cannot resolve $ref that points into a nested $defs.Fastify custom AJV does not coerce types
Fastify custom AJV does not coerce types
The server uses
strict: true, so query parameters arrive as strings. Use type: "string" with a pattern in route schemas, not type: "integer".Tests with requireApprovalAbove.valueWei: '0' must pass promptFn
Tests with requireApprovalAbove.valueWei: '0' must pass promptFn
Without passing
promptFn to buildApp(), the approval service falls through to readline and the test hangs indefinitely.Hash fixtures must stay in sync
Hash fixtures must stay in sync
When you add a new valid fixture to
tools/fixtures/valid-intents.ts, you must also add its canonical hash to tools/fixtures/hash-fixtures.ts. The canonicalization test iterates all entries.buildFromIntent() is async
buildFromIntent() is async
The 1inch swap builder returns a Promise. All call sites must
await it. Missing await will result in a [object Promise] being used as the transaction data.CJS interop requires createRequire
CJS interop requires createRequire
Packages like
ajv-formats and canonicalize do not have proper ESM exports. Always use the createRequire pattern:Questions and feedback
- Bugs and feature requests: Open an issue on GitHub with a clear description and reproduction steps.
- Environment setup problems: See the Installation Guide for prerequisites and environment variables.
- Architecture questions: See Architecture and Trust Domains.
- API Reference and TxIntent Schema for API and schema details.
Next steps
- Testing Guide — Full testing guide with fixtures and CI details
- Trust Domains — The three-domain security model
- Policy Engine — Policy rules and enforcement