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
33 changes: 33 additions & 0 deletions .claude/PROJECT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# BOJ Server - Claude Code Instructions

This repository contains the BOJ (Battle of the Judges) server application.

## Project Structure

```
boj-server/
├── .claude/ # AI assistant instructions
├── .git/ # Version control
├── .gitignore # Git ignore rules
├── .editorconfig # Editor configuration
└── ... # Server files
```

## Build Commands

Refer to project-specific documentation.

## Coding Conventions

- Follow hyperpolymath standards
- All code must have SPDX license headers
- Use approved languages only (see CLAUDE.md)
- Document all non-obvious decisions

## Security

- No hardcoded secrets
- All secrets through environment variables or secret management
- SHA-pinned dependencies where applicable
- HTTPS only, no HTTP URLs
- No MD5/SHA1 for security purposes
23 changes: 18 additions & 5 deletions cartridges/typed-wasm-mcp/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,32 @@ Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>

== Overview

Typed WASM module verifier and compiler. Validates WebAssembly type safety and compiles with type-level guarantees.
AffineScript typed-wasm gateway. Compiles `.affine` sources to Wasm with the
typed-wasm Level 7/10 ownership contract, runs the intra-module verifier
(`Tw_verify`), and runs the cross-module Level 10 boundary verifier
(`Tw_interface`) between two compiled modules.

== Tools (3)
Inputs are always `.affine` source paths — there is no path that ingests
arbitrary `.wasm`, because the verifier is meaningful only against a Wasm
module that the AffineScript compiler emitted with an
`affinescript.ownership` custom section.

== Tools (4)

[cols="2,4"]
|===
| Tool | Description

| `typed_wasm_validate_module` | Validate a WASM module file for structural correctness and type safety.
| `typed_wasm_check_types` | Run the typed-WASM type checker on a module.
| `typed_wasm_compile_module` | Compile a source file to WASM with typed-WASM guarantees.
| `typed_wasm_validate_module` | Compile a `.affine` source and run the intra-module Level 7/10 verifier against the emitted Wasm. Returns `valid=true` when every `[own]` param is consumed exactly once on every path and every `[mut]` param is exclusively held.
| `typed_wasm_check_types` | Run the AffineScript type checker on a `.affine` source. Reports type errors only — no Wasm emission, no ownership verification.
| `typed_wasm_compile_module` | Compile a `.affine` source to `.wasm`. The emitted module carries the `[affinescript.ownership]` custom section that downstream verifiers consume. Optional `output_path`; defaults to the source path with `.wasm` extension.
| `typed_wasm_verify_boundary` | Cross-module Level 10 boundary check. Compiles a callee/caller pair and verifies that the caller's local functions invoke each Linear-param import exactly once per execution path.
|===

The gateway shells out to the `affinescript` binary on `PATH` (override via
the `AFFINESCRIPT_BIN` env var). Boundary verification depends on the
cross-module import emission landed in affinescript on 2026-05-03.

== Architecture

Three-layer stack per the BoJ cartridge standard:
Expand Down
41 changes: 31 additions & 10 deletions cartridges/typed-wasm-mcp/cartridge.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
"spdx": "PMPL-1.0-or-later",
"copyright": "Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath)",
"name": "typed-wasm-mcp",
"version": "0.1.0",
"description": "Typed WASM module verifier and compiler. Validates WebAssembly type safety and compiles with type-level guarantees.",
"version": "0.2.0",
"description": "AffineScript typed-wasm gateway. Compiles .affine source to Wasm with the typed-wasm Level 7/10 ownership contract, runs the intra-module verifier, and runs the cross-module boundary verifier between two compiled modules.",
"domain": "Compiler",
"tier": "Ayo",
"protocols": [
Expand All @@ -23,13 +23,13 @@
"tools": [
{
"name": "typed_wasm_validate_module",
"description": "Validate a WASM module file for structural correctness and type safety.",
"description": "Compile a .affine source and run the intra-module typed-wasm Level 7/10 verifier (Tw_verify) against the emitted Wasm. Returns valid=true when every [own] param is consumed exactly once on every path and every [mut] param is exclusively held; otherwise valid=false with a per-violation report.",
"inputSchema": {
"type": "object",
"properties": {
"module_path": {
"type": "string",
"description": "Absolute path to the .wasm file"
"description": "Path to the .affine source file"
}
},
"required": [
Expand All @@ -39,13 +39,13 @@
},
{
"name": "typed_wasm_check_types",
"description": "Run the typed-WASM type checker on a module.",
"description": "Run the AffineScript type checker on a .affine source. Reports type errors without emitting Wasm or running the ownership verifier.",
"inputSchema": {
"type": "object",
"properties": {
"module_path": {
"type": "string",
"description": "Absolute path to the .wasm or source file"
"description": "Path to the .affine source file"
}
},
"required": [
Expand All @@ -55,23 +55,44 @@
},
{
"name": "typed_wasm_compile_module",
"description": "Compile a source file to WASM with typed-WASM guarantees.",
"description": "Compile a .affine source to Wasm. The emitted module carries the [affinescript.ownership] custom section that downstream typed-wasm verifiers consume.",
"inputSchema": {
"type": "object",
"properties": {
"module_path": {
"type": "string",
"description": "Absolute path to the source file"
"description": "Path to the .affine source file"
},
"target": {
"output_path": {
"type": "string",
"description": "Compilation target: wasm32 (default) or wasm64"
"description": "Optional path for the .wasm output. Defaults to <module_path with .wasm extension>."
}
},
"required": [
"module_path"
]
}
},
{
"name": "typed_wasm_verify_boundary",
"description": "Run the cross-module typed-wasm Level 10 boundary verifier on a callee/caller pair. Compiles both .affine sources, extracts the callee's ownership-annotated export interface, and checks that every local function in the caller invokes each Linear-param import exactly once per execution path. Returns clean=true on success; otherwise clean=false with the boundary violations.",
"inputSchema": {
"type": "object",
"properties": {
"callee_path": {
"type": "string",
"description": "Path to the .affine source for the exporter (whose pub fns define the contract)"
},
"caller_path": {
"type": "string",
"description": "Path to the .affine source for the importer (whose function bodies are checked against the callee's contract)"
}
},
"required": [
"callee_path",
"caller_path"
]
}
}
],
"ffi": {
Expand Down
151 changes: 129 additions & 22 deletions cartridges/typed-wasm-mcp/mod.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,146 @@
// SPDX-License-Identifier: PMPL-1.0-or-later
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
//
// typed-wasm-mcp/mod.js -- typed-wasm-mcp gateway. Delegates to the `typed-wasm` CLI binary.
// typed-wasm-mcp/mod.js — MCP gateway. Delegates to the `affinescript` CLI,
// which since 2026-05-03 carries the full typed-wasm Level 7/10 verifier
// (intra-module ownership) and the Level 10 cross-module boundary verifier.
//
// Tools:
// typed_wasm_validate_module → affinescript verify FILE.affine
// typed_wasm_check_types → affinescript check FILE.affine
// typed_wasm_compile_module → affinescript compile FILE.affine -o OUT.wasm
//
// All three accept .affine source paths. The verifier runs over the freshly
// emitted Wasm so there is no separate .wasm-input path: shipping unchecked
// .wasm and asking us to validate it would defeat the typed-wasm contract.
//
// Pass an explicit `output_path` to typed_wasm_compile_module; otherwise the
// .wasm is written next to the source.

const AFFINESCRIPT_BIN = Deno.env.get("AFFINESCRIPT_BIN") ?? "affinescript";

function dirOf(path) {
const i = path.lastIndexOf("/");
return i > 0 ? path.slice(0, i) : ".";
}

// `cwd` is set to the source's directory because affinescript's Module_loader
// resolves `use Foo.Bar` against the current working directory, not the source
// file's location.
async function runAffinescript(args, cwd) {
try {
const cmd = new Deno.Command(AFFINESCRIPT_BIN, {
args,
cwd,
stdout: "piped",
stderr: "piped",
});
const out = await cmd.output();
const stdout = new TextDecoder().decode(out.stdout);
const stderr = new TextDecoder().decode(out.stderr);
return { code: out.code, stdout, stderr };
} catch (e) {
return { code: -1, stdout: "", stderr: `failed to spawn ${AFFINESCRIPT_BIN}: ${e.message}` };
}
}

function requireString(args, key) {
const v = args?.[key];
if (typeof v !== "string" || v.length === 0) {
return { error: `${key} (string) is required` };
}
return { value: v };
}

function deriveWasmPath(srcPath) {
return srcPath.endsWith(".affine")
? srcPath.slice(0, -".affine".length) + ".wasm"
: srcPath + ".wasm";
}

export async function handleTool(toolName, args) {
switch (toolName) {
case "typed_wasm_validate_module": {
const { module_path } = args ?? {};
if (!module_path) return { status: 400, data: { error: "module_path is required" } };
const cmd = new Deno.Command("typed-wasm", { args: ["validate-module", String(module_path)], stdout: "piped", stderr: "piped" });
const out = await cmd.output();
if (!out.success) return { status: 500, data: { success: false, error: new TextDecoder().decode(out.stderr) } };
const stdout = new TextDecoder().decode(out.stdout);
try { return { status: 200, data: JSON.parse(stdout) }; } catch { return { status: 200, data: { success: true, output: stdout } }; }
// Compiles + runs the intra-module Level 7/10 verifier (Tw_verify).
// "Validation" = the emitted Wasm satisfies the typed-wasm ownership
// contract declared in the source.
const r = requireString(args, "module_path");
if (r.error) return { status: 400, data: { error: r.error } };
const out = await runAffinescript(["verify", r.value], dirOf(r.value));
const valid = out.code === 0;
return {
status: valid ? 200 : 200,
data: {
valid,
report: out.stdout.trim(),
diagnostics: out.stderr.trim() || undefined,
},
};
}

case "typed_wasm_check_types": {
const { module_path } = args ?? {};
if (!module_path) return { status: 400, data: { error: "module_path is required" } };
const cmd = new Deno.Command("typed-wasm", { args: ["check-types", String(module_path)], stdout: "piped", stderr: "piped" });
const out = await cmd.output();
if (!out.success) return { status: 500, data: { success: false, error: new TextDecoder().decode(out.stderr) } };
const stdout = new TextDecoder().decode(out.stdout);
try { return { status: 200, data: JSON.parse(stdout) }; } catch { return { status: 200, data: { success: true, output: stdout } }; }
// Runs only the type checker (no Wasm emission).
const r = requireString(args, "module_path");
if (r.error) return { status: 400, data: { error: r.error } };
const out = await runAffinescript(["check", r.value], dirOf(r.value));
const ok = out.code === 0;
return {
status: 200,
data: {
ok,
report: out.stdout.trim(),
errors: ok ? [] : (out.stderr.trim() ? [out.stderr.trim()] : []),
},
};
}

case "typed_wasm_compile_module": {
const { module_path, target } = args ?? {};
if (!module_path) return { status: 400, data: { error: "module_path is required" } };
const cmd = new Deno.Command("typed-wasm", { args: ["compile-module", String(module_path)], stdout: "piped", stderr: "piped" });
const out = await cmd.output();
if (!out.success) return { status: 500, data: { success: false, error: new TextDecoder().decode(out.stderr) } };
const stdout = new TextDecoder().decode(out.stdout);
try { return { status: 200, data: JSON.parse(stdout) }; } catch { return { status: 200, data: { success: true, output: stdout } }; }
// Compiles .affine → .wasm. The emitted module carries the
// [affinescript.ownership] custom section consumed by typed-wasm
// intra- and cross-module verifiers.
const r = requireString(args, "module_path");
if (r.error) return { status: 400, data: { error: r.error } };
const outputPath = (typeof args?.output_path === "string" && args.output_path)
? args.output_path
: deriveWasmPath(r.value);
const out = await runAffinescript(["compile", r.value, "-o", outputPath], dirOf(r.value));
const success = out.code === 0;
return {
status: success ? 200 : 200,
data: {
success,
output_path: success ? outputPath : undefined,
report: out.stdout.trim(),
diagnostics: out.stderr.trim() || undefined,
},
};
}

case "typed_wasm_verify_boundary": {
// Cross-module Level 10 boundary check. Compiles both files, then
// verifies that the caller's local funcs respect the ownership
// contract declared by the callee's Wasm exports.
const callee = requireString(args, "callee_path");
if (callee.error) return { status: 400, data: { error: callee.error } };
const caller = requireString(args, "caller_path");
if (caller.error) return { status: 400, data: { error: caller.error } };
// Verify-boundary needs to find both files' imports — run from the
// caller's directory (most likely the place where Callee lives too).
const out = await runAffinescript(
["verify-boundary", callee.value, caller.value],
dirOf(caller.value),
);
const clean = out.code === 0;
return {
status: 200,
data: {
clean,
report: out.stdout.trim(),
diagnostics: out.stderr.trim() || undefined,
},
};
}

default:
return { status: 404, data: { error: `Unknown tool: ${toolName}` } };
}
Expand Down
Loading