Surfaces
此内容尚不支持你的语言。
A machine that runs is useful. A machine that is reachable is powerful. The expresses section declares how a machine is reachable by the outside world: as an API endpoint, a webhook receiver, a WebSocket connection, an A2A agent, or a web page.
Every surface inherits the machine’s governance. When a machine is invoked through any surface, every action still produces an intent that is mediated by the governance interpreter. A machine that cannot send email cannot send email regardless of whether it was invoked via Siri, a REST call, or another agent. The governance boundary does not move when the invocation surface changes.
MCP is implicit
Every machine is automatically available as an MCP tool. You do not need to declare anything in expresses for MCP to work. This is the zero-configuration default: write a machine, and it is immediately usable by Claude, Cursor, ChatGPT, VS Code, or any MCP-compatible client.
The expresses section is for declaring surfaces beyond MCP.
Declaring surfaces
machine my_service
accepts query as text, is required
responds with results as list
implements ask search, using: "anthropic:claude-sonnet-4-6" with task "Search for: ${input.query}" returns results as list assuming results: ["result 1", "result 2"]
expresses api path: "/api/search" method: "POST" auth: oauth, api_key scopes: machines:run webhook path: "/hooks/incoming" auth: signature websocket path: "/ws/updates" auth: oauth, session a2a auth: oauth page path: "/search" title: "Search" auth: sessionThis machine is reachable six ways: MCP (implicit), REST API, webhook, WebSocket, A2A, and web page. All six surfaces invoke the same machine, respect the same governance, and record to the same behavioral ledger.
Surface types at a glance
| Surface | Use case | Configuration |
|---|---|---|
| MCP | AI tool calling (Claude, Cursor, etc.) | Implicit, no config needed |
| API | REST endpoints for HTTP clients | Path, method, authentication |
| Webhook | Receiving events from external services | Path, provider, signature verification |
| WebSocket | Real-time bidirectional communication | Path |
| A2A | Agent-to-Agent protocol for multi-agent systems | Methods |
| Page | Web pages for end users (Kanvas) | Path, title |
Each surface type has its own guide page with full examples:
- MCP - Model Context Protocol integration
- REST API - HTTP endpoints
- Webhooks - Receiving external events
- WebSocket - Real-time connections
- A2A - Agent-to-Agent protocol
- Pages - Web interfaces (Kanvas)
Surface authentication
Every surface supports auth:, scopes:, and allow: config keys. When omitted, each surface type gets a secure default:
| Surface | Default auth | Why |
|---|---|---|
| API | oauth, api_key | Machine callers use either |
| MCP | oauth | MCP spec expects OAuth |
| Page | session | Humans use browsers |
| Webhook | signature | Webhooks use HMAC |
| WebSocket | oauth, session | Either machine or browser |
| A2A | oauth | Agent-to-agent uses OAuth |
To make a surface public, write auth: public explicitly. To restrict access to specific users or organizations, add an allow: list:
expresses api path: "/api/data" auth: oauth, api_key scopes: machines:run allow: @acme.com, [email protected], @*.corp.acme.comThe allow: list supports exact emails, exact domains (@acme.com), wildcard subdomains (@*.acme.com), and OAuth client IDs (client:oac2_xxx).
Dev mode: On localhost, surface auth is skipped entirely. No OAuth setup needed for local development.
See the expresses reference for full details.
Governance across surfaces
Every surface access is recorded as a SurfaceAccess event in the behavioral ledger. The event includes:
- Which surface was used (
:mcp,:api,:webhook,:websocket,:a2a,:page) - Who made the request (authentication identity)
- What inputs were provided
- The governance decision (allowed/denied) and which rules matched
This means you can answer questions like “how many times was this machine invoked via the API vs. via MCP?” or “which webhook deliveries were denied by governance?”
Authentication
Surfaces that accept external requests support authentication:
| Auth type | How it works |
|---|---|
"bearer_token" | Authorization: Bearer <token> header |
"api_key" | X-Api-Key header or ?api_key= query parameter |
"hmac_sha256" | Signature verification (for webhooks) |
"none" | No authentication (default) |
Authentication determines who is making the request. Governance determines what the machine is allowed to do. These are separate concerns: a properly authenticated request can still be denied by governance if the machine does not have permission for the requested action.
The tunnel
Local machines are not directly reachable from the internet. The Kortex tunnel solves this by relaying requests through mashin.live to your local cell. Any machine with an expresses section is automatically exposed through the tunnel when your cell starts.
See Cells: Exposing local machines for details.
Next steps
- MCP - The implicit default surface
- Building Effect Machines - Create new effects in any language
- expresses reference - Full syntax specification