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
1
Fork and clone
2
Install dependencies
Requires Node.js >= 20 and npm >= 9.
3
Build all packages
TypeScript project references, compiled in dependency order.
4
Run all tests
Confirm a clean baseline.
5
Verify the server starts
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.Domain B — Trusted
The secure core. Keys, policy enforcement, signing, audit logging, RPC access.Domain C — Limited Trust
Sandboxed execution. No key access, API-only communication with Core.Tooling
Additional directories:
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
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).
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
1
Create a feature branch
2
Write code and tests
Follow the coding standards and testing requirements above.
3
Run linting and formatting checks
4
Run the full test suite
npm run test:security.5
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.
6
PR review
Security-sensitive changes (Domain B, key management, policy, approval) require extra scrutiny and may take longer to review.
7
Merge to main
After approval.
Commit style
The project follows Conventional Commits. Use the appropriate prefix for each commit.
Keep commit messages concise. The first line should be under 72 characters. Use the body for additional context when needed.
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