Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 52 additions & 9 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,13 +1,56 @@
# Required runtime signing config (Railway canonical setup)
RECEIPT_SIGNING_PRIVATE_KEY_PEM_B64=REPLACE_WITH_BASE64_OF_PKCS8_PEM_PRIVATE_KEY
RECEIPT_SIGNING_PUBLIC_KEY_B64=hhyCuPNoMk4JtEvGEV8F6nMZ4uDO1EcyizPufmnJTOY=
RECEIPT_SIGNER_ID=runtime.commandlayer.eth
# -----------------------------------------------
# CommandLayer Runtime — Environment Variables
# -----------------------------------------------

# Optional (required only for /verify?ens=1)
ETH_RPC_URL=
# --- Required for production receipt signing ---

HOST=0.0.0.0
# ENS name of the signer (e.g. "runtime.commandlayer.eth")
CL_RECEIPT_SIGNER_ID=runtime.commandlayer.eth

# Ed25519 private key in PKCS8 PEM format, backslash-n encoded for single-line .env
# Generate: openssl genpkey -algorithm ed25519
RECEIPT_SIGNING_PRIVATE_KEY_PEM=-----BEGIN PRIVATE KEY-----\nMC4...\n-----END PRIVATE KEY-----

# Ed25519 public key — raw 32 bytes as base64 (recommended) or SPKI PEM
# Generate: openssl pkey -pubout -outform DER | tail -c 32 | base64
RECEIPT_SIGNING_PUBLIC_KEY_B64=<32-byte-pubkey-base64>

# --- ENS verification (optional) ---

# Ethereum JSON-RPC URL for ENS resolution (mainnet)
# ETH_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY

# ENS TXT key names (defaults match CommandLayer spec)
# ENS_SIG_PUB_KEY=cl.sig.pub
# ENS_SIG_KID_KEY=cl.sig.kid
# ENS_SIG_CANONICAL_KEY=cl.sig.canonical

# --- Server ---
PORT=8080
ENABLED_VERBS=fetch,describe,format,clean,parse,summarize,convert,explain,analyze,classify
HOST=0.0.0.0
# SERVICE_NAME=commandlayer-runtime
# SERVICE_VERSION=1.1.0
# API_VERSION=1.1.0
# CANONICAL_BASE_URL=https://runtime.commandlayer.org

# --- Rate limiting ---
# RATE_LIMIT_WINDOW_MS=60000
# RATE_LIMIT_MAX=120

# --- Schema validation ---
# SCHEMA_HOST=https://www.commandlayer.org
# SCHEMA_FETCH_TIMEOUT_MS=15000
# VERIFY_SCHEMA_CACHED_ONLY=1

# --- Fetch verb hardening ---
# FETCH_TIMEOUT_MS=8000
# FETCH_MAX_BYTES=262144
# ENABLE_SSRF_GUARD=1
# ALLOW_FETCH_HOSTS=

# --- Debug (never enable in production) ---
# ENABLE_DEBUG=0
# DEBUG_TOKEN=

# Optional: DEV_AUTO_KEYS=1 (development only, in-memory ephemeral keypair)
# --- Dev only (generates ephemeral keypair, NEVER use in production) ---
# DEV_AUTO_KEYS=0
59 changes: 59 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Contributing to CommandLayer Runtime

## Prerequisites

- Node.js >= 20
- An Ed25519 keypair (generate via `DEV_AUTO_KEYS=1 node server.mjs`)

## Local Development

```bash
cp .env.example .env
# Edit .env with your keys, or set DEV_AUTO_KEYS=1 for ephemeral dev keys
npm install
npm run check # syntax check
npm run test:unit
```

## Protocol Spec

All receipts emitted by this runtime conform to CommandLayer Receipt v1.1.0:

- **Signing**: `Ed25519(UTF8(canonicalize(payload)))`
- **Canonicalization**: `json.sorted_keys.v1` (recursive sorted-keys JSON)
- **Proof fields**: `alg`, `canonical`, `signer_id`, `kid`, `signature`

The `alg` value is `"ed25519"`. Legacy receipts using `"ed25519-sha256"` and
`signature_b64`/`hash_sha256` are accepted at `/verify` for backward compatibility.

The `/verify` route accepts both:
- v1.1.0: `proof.signature` (preferred)
- legacy: `proof.signature_b64` (backward compat)

## Env Variables

See `.env.example` for the full list.

## Tests

```bash
npm test # unit + smoke
npm run test:unit # unit only (runtime/tests/*.test.mjs)
```

## Rate Limiting

The runtime includes a built-in in-memory rate limiter (default: 120 req/min per IP).
Configure via `RATE_LIMIT_MAX` and `RATE_LIMIT_WINDOW_MS`. For multi-instance deployments,
replace `src/middleware/rateLimit.mjs` with express-rate-limit + a Redis store.

## Submitting Changes

1. Branch from `main`
2. `npm run check && npm test` must pass
3. For protocol changes, update `CHANGELOG.md` and add a test vector to `test_vectors/`
4. Open a PR with a clear description

## Security

See [SECURITY.md](SECURITY.md) for the vulnerability disclosure policy.
22 changes: 17 additions & 5 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
FROM node:20-bookworm-slim
# syntax=docker/dockerfile:1.7
FROM node:20-bookworm-slim AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force

FROM node:20-bookworm-slim AS runtime
WORKDIR /app
ENV NODE_ENV=production

COPY package.json package-lock.json ./
RUN npm ci --omit=dev && npm cache clean --force
# Create writable temp dir before switching to non-root user
RUN mkdir -p /app/src && chown -R node:node /app

# Run as non-root for container security
USER node

COPY server.mjs ./
COPY --from=deps --chown=node:node /app/node_modules ./node_modules
COPY --chown=node:node server.mjs ./
COPY --chown=node:node src/ ./src/

EXPOSE 8080
CMD ["npm", "start"]
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node -e "fetch('http://localhost:8080/healthz').then(r=>r.ok?process.exit(0):process.exit(1)).catch(()=>process.exit(1))"
CMD ["node", "server.mjs"]
187 changes: 1 addition & 186 deletions agent_log.json
Original file line number Diff line number Diff line change
@@ -1,186 +1 @@
{
"agent": "CommandLayer",
"agent_id": 33370,
"operator_wallet": "0x6FFa1e00509d8B625c2F061D7dB07893B37199BC",
"erc8004_registration_tx": "0xb511007618f8c0aa0b5c12b48084ce67dc52321a79e0ef9002fdc8e6db5e899d",
"hackathon": "Synthesis 2026",
"log_version": "1.0.0",
"generated_at": "2026-03-22T05:30:00Z",
"execution_log": [
{
"step": 1,
"timestamp": "2026-03-22T00:00:00Z",
"action": "cross_repo_audit",
"description": "Audited all 8 CommandLayer repositories for cross-repo coherence, version alignment, and hackathon readiness",
"tool_calls": [
"web_fetch: github.com/commandlayer/runtime",
"web_fetch: github.com/commandlayer/protocol-commons",
"web_fetch: github.com/commandlayer/protocol-commercial",
"web_fetch: github.com/commandlayer/agent-cards",
"web_fetch: github.com/commandlayer/sdk",
"web_fetch: github.com/commandlayer/runtime-core",
"web_fetch: github.com/commandlayer/commercial-runtime",
"web_fetch: github.com/commandlayer/commandlayer-org"
],
"decision": "Identified stale README content in protocol-commons and protocol-commercial; identified missing repo descriptions on runtime-core and commercial-runtime; identified SDK not published to npm or PyPI",
"outcome": "Audit complete — priority fix list generated",
"status": "success"
},
{
"step": 2,
"timestamp": "2026-03-22T01:00:00Z",
"action": "verify_runtime_health",
"description": "Confirmed live runtime status, signer identity, and ENS key resolution",
"tool_calls": [
"curl: GET https://runtime.commandlayer.org/health"
],
"decision": "Runtime confirmed live — signer_ok: true, verifier_ok: true, signer_id: runtime.commandlayer.eth, kid: vC4WbcNoq2znSCiQ",
"outcome": "Runtime healthy and signing",
"status": "success",
"evidence": {
"endpoint": "https://runtime.commandlayer.org/health",
"signer_id": "runtime.commandlayer.eth",
"signer_ok": true,
"verifier_ok": true,
"version": "1.1.0"
}
},
{
"step": 3,
"timestamp": "2026-03-22T02:00:00Z",
"action": "execute_verb_and_verify_receipt",
"description": "Executed summarize verb and verified signed receipt returned from runtime",
"tool_calls": [
"curl: POST https://runtime.commandlayer.org/summarize/v1.1.0"
],
"decision": "Receipt returned with valid Ed25519 signature, hash, and signer identity",
"outcome": "Live signed receipt produced and verified",
"status": "success",
"evidence": {
"receipt_id": "clrcpt_3aeed5c2f79e419ea2925fd69522ac71",
"trace_id": "cltrace_6991dc5194504516b687559470e1f168",
"verb": "summarize",
"version": "1.1.0",
"status": "success",
"signer_id": "runtime.commandlayer.eth",
"alg": "ed25519-sha256",
"hash_sha256": "79eb8f7581e9767e7bd0f4eb28ce6d6d5a7ab44e4f46d508cd706821cdbe7fbe",
"signature_b64": "J7Gx4QvHw7iP9fvl9qxc752wUtrIIcRhJTJKdim9Sm59QxsM0FRlwNFocgtGo4JRmKhHod5UdDivx6ln7sgrBw=="
}
},
{
"step": 4,
"timestamp": "2026-03-22T02:30:00Z",
"action": "publish_typescript_sdk",
"description": "Built and published @commandlayer/sdk@1.1.0 to npm",
"tool_calls": [
"npm ci",
"npm audit fix",
"npm run build",
"npm publish --access public"
],
"decision": "0 vulnerabilities after audit fix — safe to publish",
"outcome": "@commandlayer/sdk@1.1.0 published to npm registry",
"status": "success",
"evidence": {
"package": "@commandlayer/sdk",
"version": "1.1.0",
"registry": "https://registry.npmjs.org/",
"vulnerabilities": 0,
"files": 10,
"unpacked_size_kb": 182
}
},
{
"step": 5,
"timestamp": "2026-03-22T03:00:00Z",
"action": "publish_python_sdk",
"description": "Built and published commandlayer==1.1.0 to PyPI",
"tool_calls": [
"python -m build",
"python -m twine upload dist/*"
],
"decision": "Package built cleanly — publish to PyPI",
"outcome": "commandlayer@1.1.0 published to PyPI",
"status": "success",
"evidence": {
"package": "commandlayer",
"version": "1.1.0",
"registry": "https://pypi.org/project/commandlayer/1.1.0/"
}
},
{
"step": 6,
"timestamp": "2026-03-22T04:00:00Z",
"action": "verify_erc8004_registration",
"description": "Confirmed ERC-8004 registration on Base mainnet",
"tool_calls": [
"web_fetch: https://basescan.org/tx/0xb511007618f8c0aa0b5c12b48084ce67dc52321a79e0ef9002fdc8e6db5e899d"
],
"decision": "Registration confirmed — agent_id 33370, identity registry 0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
"outcome": "ERC-8004 identity verified onchain",
"status": "success",
"evidence": {
"tx": "0xb511007618f8c0aa0b5c12b48084ce67dc52321a79e0ef9002fdc8e6db5e899d",
"agent_id": 33370,
"identity_registry": "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
"chain": "base",
"block": 43509626,
"status": "success"
}
},
{
"step": 7,
"timestamp": "2026-03-22T05:00:00Z",
"action": "self_custody_transfer",
"description": "Transferred hackathon ERC-8004 NFT to self-custody wallet for submission publishing",
"tool_calls": [
"curl: POST https://synthesis.devfolio.co/participants/me/transfer/init",
"curl: POST https://synthesis.devfolio.co/participants/me/transfer/confirm"
],
"decision": "Transfer to burner wallet for hackathon NFT custody",
"outcome": "Self-custody transfer complete",
"status": "success",
"evidence": {
"tx": "0xe9c8b5134e09b71b1ec62733483dab00cfd84592cf44251b84cf698d8822c165",
"custody_type": "self_custody",
"owner_address": "0x6A329F25b5b951Ea283FDa4473aB3453215D1D14"
}
},
{
"step": 8,
"timestamp": "2026-03-22T05:27:18Z",
"action": "submit_hackathon_project",
"description": "Created project draft via Synthesis API across 8 tracks",
"tool_calls": [
"curl: GET https://synthesis.devfolio.co/catalog",
"curl: POST https://synthesis.devfolio.co/projects",
"curl: POST https://synthesis.devfolio.co/projects/c7321290a59e43b786aaec48a0e6c9c8"
],
"decision": "Submit to Protocol Labs ERC-8004, Protocol Labs Let the Agent Cook, Base Agent Services, OpenServ, ENS Identity, ENS Open Integration, ENS Communication, Synthesis Open Track",
"outcome": "Draft project created — project UUID c7321290a59e43b786aaec48a0e6c9c8",
"status": "success",
"evidence": {
"project_uuid": "c7321290a59e43b786aaec48a0e6c9c8",
"slug": "commandlayer-d982",
"tracks": 8,
"status": "draft"
}
}
],
"summary": {
"total_steps": 8,
"successful": 8,
"failed": 0,
"tool_calls_total": 22,
"autonomous_decisions": 8,
"onchain_artifacts": [
"0xb511007618f8c0aa0b5c12b48084ce67dc52321a79e0ef9002fdc8e6db5e899d",
"0xe9c8b5134e09b71b1ec62733483dab00cfd84592cf44251b84cf698d8822c165"
],
"packages_published": [
"@commandlayer/sdk@1.1.0",
"commandlayer==1.1.0"
]
}
}
[]
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"test:unit": "node --test runtime/tests/*.test.mjs"
},
"dependencies": {
"@commandlayer/runtime-core": "github:commandlayer/runtime-core#main",
"@commandlayer/runtime-core": "github:commandlayer/runtime-core#v1.1.0",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"ethers": "^6.16.0",
Expand Down
Loading
Loading