Skip to main content

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.
If you are unsure whether a proposed change fits the architecture, open an issue to discuss before writing code.

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

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.

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

  • Strict mode is enforced (strict: true in the root tsconfig, plus noUncheckedIndexedAccess, noUnusedLocals, noUnusedParameters).
  • ESM with Node16 module resolution. All packages use "type": "module" and the Node16 module/moduleResolution settings.
  • additionalProperties: false on 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:check before submitting.
  • ESLint for linting. Run npm run lint before submitting.
  • Follow the formatting conventions already present in the codebase. When in doubt, let Prettier decide.

Testing requirements

All pull requests must satisfy:
  1. npm test passes — this runs unit and integration tests.
  2. New features include unit tests. If you add a builder, service, route, or adapter method, add corresponding tests.
  3. Fund-affecting features include security tests. Changes to signing, policy enforcement, approval flow, or key management must include tests that verify Domain B integrity.
  4. Mock RPC factories implement all RpcClient methods, including readNativeBalance. Incomplete mocks cause runtime failures in unrelated tests.
  5. Test fixtures live in tools/fixtures/. When adding a new valid fixture, also add its pre-computed hash to hash-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.
These invariants are non-negotiable. Any PR that weakens any of them will not be merged.
  1. Private keys exist only in Domain B — never in Domain A (skills/adapters) or Domain C (sandbox).
  2. Every signature passes PolicyEngine + Preflight — there are no bypass paths.
  3. Skills have no direct RPC access — only ISCL Core contacts the blockchain.
  4. All fund-affecting operations use TxIntent v1 — no arbitrary calldata signing.
  5. All critical steps are audit logged — correlated by intentId.
  6. 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).
If your change touches signing, key management, policy enforcement, or approval flow, flag it in the PR description. These changes receive extra review scrutiny.

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

All tests must pass. If you have Docker available, also run 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.
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.
When embedding the schema in a wrapper object, hoist $defs to the wrapper root. AJV cannot resolve $ref that points into a nested $defs.
The server uses strict: true, so query parameters arrive as strings. Use type: "string" with a pattern in route schemas, not type: "integer".
Without passing promptFn to buildApp(), the approval service falls through to readline and the test hangs indefinitely.
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.
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.
Packages like ajv-formats and canonicalize do not have proper ESM exports. Always use the createRequire pattern:

Questions and feedback

Next steps