Using AI Coding Assistants - Terminal 3 Documentation

Install it

Save the file below as .claude/skills/t3n-adk-quickstart/SKILL.md in your project (or your global ~/.claude/skills/ directory), then invoke it with /t3n-adk-quickstart or just describe what you’re building — Claude Code will pick it up automatically based on the skill’s description.

Save everything below the frontmatter (the --- block at the top) as AGENTS.md in your project root — Codex CLI and Codex cloud agents read this automatically at the start of a session.

Save everything below the frontmatter (the --- block at the top) as .cursorrules in your project root.

Paste the body of the file (below the frontmatter) as a system-context or rules file, using whatever mechanism your tool supports for persistent project instructions.

Full skill file — t3n-adk-quickstart/SKILL.md

---
name: t3n-adk-quickstart
description: Scaffold and debug a Terminal 3 (T3N) ADK integration — use when the user wants to "build with Terminal 3", "use the T3N SDK", or "add a TEE contract" to their app.
---

# Building with Terminal 3 ADK

You are helping a developer — possibly a first-timer, possibly vibe-coding — integrate the Terminal 3 Agent Developer Kit. Follow this order. Do not jump to contract code or advanced features before step 3 actually works. Do the scaffolding yourself — don't just print code and tell the user to run it.

## Step 1: Confirm the basics before writing any contract code

Ask the user (don't assume):
1. Do they already have a T3N API key and test credits? If not, point them to the claim page linked from `/developers/adk/get-started/prerequisites/request-test-tokens` on the Terminal 3 docs — it's self-serve, no approval wait.
2. Is this a Next.js/Vite/Webpack project? If so, be ready for possible WASM-loading friction — see the troubleshooting note in `/developers/adk/get-started/quickstart`.

## Step 2: Scaffold the project BEFORE writing the connection code

A blank folder needs this before the SDK's `await`-based sample code will even parse:

```bash
mkdir -p my-t3n-app && cd my-t3n-app
npm init -y
npm pkg set type=module        # required — the sample code uses top-level await
npm install @terminal3/t3n-sdk tsx

Then get the user's T3N_API_KEY (from the claim page) and export it into the shell you'll run scripts from — don't write it into a file:

export T3N_API_KEY="<their key>"

Step 3: Get a basic authenticated connection working FIRST

Save this as quickstart.ts in the project you just scaffolded, and actually run it — don't just write it:

import {
  T3nClient,
  setEnvironment,
  loadWasmComponent,
  eth_get_address,
  metamask_sign,
  createEthAuthInput,
} from "@terminal3/t3n-sdk";

setEnvironment("testnet"); // SDK defaults to production — always set this explicitly

const T3N_API_KEY = process.env.T3N_API_KEY!;
const wasmComponent = await loadWasmComponent();
const address = eth_get_address(T3N_API_KEY);

const t3n = new T3nClient({
  wasmComponent,
  handlers: { EthSign: metamask_sign(address, undefined, T3N_API_KEY) },
});

await t3n.handshake();
const did = await t3n.authenticate(createEthAuthInput(address));
const tenantDid = did.value; // did:t3n:... — keep this variable, reuse it in every later step
console.log("Connected as:", tenantDid);

Run with npx tsx quickstart.ts. Do not proceed to contract code until this prints a did:t3n:... value.

Keep building in this same file. t3n and tenantDid are plain JS variables scoped to this one running script. Later snippets (TenantClient construction, contract registration, invocation) all assume t3n/tenantDid/tenant are already in scope — append new code to the bottom of quickstart.ts rather than starting fresh files, unless you deliberately repeat the connect-and-authenticate block at the top of a new one.

Step 4: Contract code lives in a SEPARATE project

The Rust/WASM contract is not part of the Node project above — it's its own crate. Clone the reference implementation instead of hand-typing it, as a sibling folder (not nested inside your Node project):

cd ..
git clone https://github.com/Terminal-3/z-tenant-flight.git
cd z-tenant-flight
rustup target add wasm32-wasip2   # install rustup first via https://rustup.rs if `rustup`/`cargo` aren't found
cargo build --target wasm32-wasip2 --release

This produces target/wasm32-wasip2/release/z_tenant_flight.wasm. Back in my-t3n-app/quickstart.ts for the registration step, point WASM_PATH across the two folders, e.g. ../z-tenant-flight/target/wasm32-wasip2/release/z_tenant_flight.wasm.

Step 5: Known pitfalls — check these BEFORE assuming it's your bug

Symptom Likely cause Fix
Top-level await is currently not supported with the "cjs" output format No package.json, or missing "type": "module" npm pkg set type=module (see Step 2)
Invalid Ethereum private key from eth_get_address T3N_API_KEY was never exported into the shell running the script export T3N_API_KEY="..." before running (see Step 2)
ReferenceError: tenant is not defined / tenantDid is not defined in a later snippet Each doc page's code sample assumes earlier variables are still in scope — they aren't, across separate files Append new code to the same running file instead of starting a new one (see Step 3)
ENOENT reading a .wasm file WASM_PATH is relative to the wrong project — the contract lives in its own cloned repo, not the Node project Point WASM_PATH across folders, e.g. ../z-tenant-flight/target/... (see Step 4)
WASM/module parse error, works with tsx but not node/webpack Bundler compatibility Try a plain Node script or server-only route first to isolate the bundler from the SDK
Generic HTTP 500 with no detail Could be egress not authorized, wrong action name, or a real platform issue — not necessarily your code Check the outbound-HTTP-grant requirement below before assuming it's a bug; if it's consistent and reproducible, it may be platform-side
Contract can't reach an external API even though the code looks right Outbound HTTP is authorized by the calling user's grant, not the contract itself Confirm a user (or self-grant, for direct calls) actually authorized that host via agent-auth-update
tenant not found The DID was derived/hardcoded instead of read from the session Always read tenantDid/did.value from the authenticated session, never construct it
AccessDenied reading your own contract's secret KV map ACLs default to deny Explicitly set readers/writers when creating the map
Map path looks right but reads/writes silently miss Tenant DID may have been hex-encoded twice when building the map path Use tenant_did()'s return value directly — don't wrap it in another hex::encode
Re-registering a contract breaks old pinned-version calls Registering a newer version can reroute calls that pin an older one Re-verify any version-pinned calls after any re-registration
Agent can authenticate but its calls fail with host/http.egress_denied The agent is authenticated but no one has authorized it yet — those are two different steps The user (data owner), not the agent, must sign an agent-auth-update grant naming the agent's DID before it can do anything with outbound HTTP

Step 6: Where to look things up

Step 7: When something seems broken and isn't in this list

Say so plainly to the user rather than guessing. Some rough edges are real platform issues with no clean workaround, not something you can code around. Don't fabricate a fix — point them at the developer Telegram (https://t.me/terminal3developer) or devrel@terminal3.io.


Compatibility: this file uses Claude Code's Skill frontmatter (name/description). For Codex, save everything below the frontmatter as AGENTS.md in your project root — Codex reads it automatically. For Cursor, save everything below the frontmatter as .cursorrules. For Windsurf or any other tool that accepts a plain system-context file, paste the body as-is.