Interface System
Theater's interface system is built on the WebAssembly Component Model and WebAssembly Interface Types (WIT), providing a type-safe and flexible way for actors to expose and consume functionality while maintaining verifiable state transitions.
WebAssembly Interface Types (WIT)
Theater defines its interfaces using WIT, providing a language-agnostic way to describe component interfaces. The core WIT files are located in the wit/
directory:
Core Interfaces
- actor.wit - Basic actor interface:
interface actor {
use types.{state};
init: func(state: state, params: tuple<string>) -> result<tuple<state>, string>;
}
- message-server.wit - Message handling interface:
interface message-server-client {
use types.{json, event};
handle-send: func(state: option<json>, params: tuple<json>) -> result<tuple<option<json>>, string>;
handle-request: func(state: option<json>, params: tuple<json>) -> result<tuple<option<json>, tuple<json>>, string>;
}
interface message-server-host {
use types.{json, actor-id};
send: func(actor-id: actor-id, msg: json) -> result<_, string>;
request: func(actor-id: actor-id, msg: json) -> result<json, string>;
}
- http.wit - HTTP server and client interfaces:
interface http-server {
use types.{state};
use http-types.{http-request, http-response};
handle-request: func(state: state, params: tuple<http-request>) -> result<tuple<state, tuple<http-response>>, string>;
}
interface http-client {
use types.{json};
use http-types.{http-request, http-response};
send-http: func(req: http-request) -> result<http-response, string>;
}
- supervisor.wit - Parent-child supervision:
interface supervisor {
spawn: func(manifest: string) -> result<string, string>;
list-children: func() -> list<string>;
stop-child: func(child-id: string) -> result<_, string>;
restart-child: func(child-id: string) -> result<_, string>;
get-child-state: func(child-id: string) -> result<list<u8>, string>;
get-child-events: func(child-id: string) -> result<list<chain-event>, string>;
// ...
}
- types.wit - Common data types:
interface types {
type json = list<u8>;
type state = option<list<u8>>;
type actor-id = string;
// ...
}
Handler System
Theater uses a handler system to connect actor interfaces with their implementations:
Handler Types
The current implementation includes several handler types:
-
Message Server Handler:
- Handles direct actor-to-actor messaging
- Supports both request/response and one-way sends
- Serializes messages as JSON bytes
-
HTTP Server Handler:
- Exposes actor functionality via HTTP endpoints
- Converts HTTP requests to actor messages
- Transforms responses back to HTTP
-
Supervisor Handler:
- Enables parent-child supervision
- Provides lifecycle management functions
- Access to child state and events
Handler Configuration
Handlers are configured in actor manifests:
name = "my-actor"
component_path = "my_actor.wasm"
# Message server handler
[[handlers]]
type = "message-server"
config = { port = 8080 }
interface = "ntwk:theater/message-server-client"
# HTTP server handler
[[handlers]]
type = "http-server"
config = { port = 8081 }
# Supervisor handler
[[handlers]]
type = "supervisor"
config = {}
Message Flow
Actor-to-Actor Messaging
-
Send Message (one-way):
- Sender actor calls
message-server-host::send
- Message is routed through TheaterRuntime
- Recipient actor's
handle-send
is called - State is updated and recorded in hash chain
- No response is returned to sender
- Sender actor calls
-
Request Message (request/response):
- Sender actor calls
message-server-host::request
- Message is routed through TheaterRuntime
- Recipient actor's
handle-request
is called - State is updated and recorded in hash chain
- Response is returned to sender
- Sender actor calls
HTTP Integration
-
Incoming HTTP Request:
- HTTP request arrives at server
- Request is converted to
http-request
struct - Actor's
handle-request
function is called - Response is converted back to HTTP and returned
-
Outgoing HTTP Request:
- Actor calls
http-client::send-http
- Request is made to external service
- Response is returned to actor
- Interaction is recorded in hash chain
- Actor calls
Interface Implementation
Actors implement interfaces through WebAssembly components:
Required Component Structure
A Theater actor component must:
- Implement required interfaces (based on handlers)
- Export interface functions with correct signatures
- Handle state consistently
- Process messages according to interface specifications
Example Actor Implementation
#![allow(unused)] fn main() { use theater_sdk::{actor, message_server}; struct CounterActor; #[actor::export] impl actor::Actor for CounterActor { fn init(state: Option<Vec<u8>>, params: (String,)) -> Result<(Option<Vec<u8>>,), String> { // Initialize with either existing state or new state let state = state.unwrap_or_else(|| { let initial_state = serde_json::json!({ "count": 0 }); serde_json::to_vec(&initial_state).unwrap() }); Ok((Some(state),)) } } #[message_server::export] impl message_server::MessageServerClient for CounterActor { fn handle_send( state: Option<Vec<u8>>, params: (Vec<u8>,) ) -> Result<(Option<Vec<u8>>,), String> { // Process one-way message // ... Ok((new_state,)) } fn handle_request( state: Option<Vec<u8>>, params: (Vec<u8>,) ) -> Result<(Option<Vec<u8>>, (Vec<u8>,)), String> { // Process request/response message // ... Ok((new_state, (response,))) } } }
Working with State
The interface system consistently handles state:
-
State Representation:
- State is represented as
Option<Vec<u8>>
(optional bytes) - Typically contains serialized JSON or other format
- State is passed to and from interface functions
- State is represented as
-
State Updates:
- Functions return new state
- Changes are recorded in hash chain
- State is available for inspection and verification
-
State Access:
- Current state is provided to interface functions
- Functions can modify state by returning new version
- Parent actors can access child state via supervision
Actor Manifest
The manifest connects interfaces to implementations:
name = "counter-actor"
component_path = "counter.wasm"
# Interfaces implemented by this actor
[interface]
implements = [
"ntwk:theater/actor",
"ntwk:theater/message-server-client",
"ntwk:theater/http-server"
]
# Interfaces required by this actor
requires = [
"ntwk:theater/message-server-host"
]
# Message server handler
[[handlers]]
type = "message-server"
config = {}
# HTTP server handler
[[handlers]]
type = "http-server"
config = { port = 8080 }
Interface Composition
Theater's interface system is designed for composition, allowing actors to:
-
Implement Multiple Interfaces:
- Core actor functionality
- Message handling
- HTTP serving
- Custom functionality
-
Depend on Host Interfaces:
- Message sending
- HTTP client
- Supervision
- File system access
-
Combine Interface Types:
- One interface can extend another
- Interfaces can share common types
- Versioning through interface namespaces
Each interface maintains state chain integrity while providing a specific capability.
Message Structure
While the interface system is flexible, messages typically follow a standard structure:
{
"type": "request_type",
"action": "specific_operation",
"payload": {
"param1": "value1",
"param2": 42
},
"metadata": {
"timestamp": "2025-02-26T12:34:56Z",
"request_id": "req-123456"
}
}
Responses typically include:
{
"type": "response",
"status": "success",
"payload": {
"result": "value"
},
"metadata": {
"timestamp": "2025-02-26T12:34:57Z",
"request_id": "req-123456"
}
}
Debugging Interfaces
Theater provides several mechanisms for debugging interfaces:
-
Tracing:
- All interface calls are logged
- State transitions are recorded
- Message flow can be traced end-to-end
-
Interface Inspection:
- WIT interfaces can be introspected
- Available functions can be listed
- Type checking for message formats
-
State Verification:
- Hash chain can be verified at any point
- State history can be examined
- State transitions can be replayed
Custom Interface Development
Creating new interfaces requires:
-
WIT Definition:
- Define interface functions and types
- Document expected behavior
- Specify state handling patterns
-
Handler Implementation:
- Create handler in Theater runtime
- Connect WIT interface to actor
- Handle message routing correctly
-
Actor Implementation:
- Implement interface functions
- Handle state properly
- Process messages according to spec
Best Practices
-
Interface Design
- Keep interfaces focused on single responsibility
- Use clear, descriptive function names
- Document expected behavior
- Provide meaningful error messages
- Consider versioning strategy
-
Message Design
- Use consistent type field for categorization
- Include action field for specific operations
- Structure payloads logically
- Add metadata for debugging
- Handle errors consistently
-
State Management
- Keep state serializable
- Handle state transitions atomically
- Validate state after changes
- Consider state size impacts
- Test state rollback scenarios
-
Security Considerations
- Validate all input messages
- Sanitize data crossing interface boundaries
- Control access to sensitive interfaces
- Verify state integrity frequently
- Test for message injection risks