Lightweight
WebAssembly, not Linux: each VM has private memory, files and environment, with no guest OS. Code can be tens of kilobytes; a VM starts in under 1 ms in the best case scenario.
Golem runs AI agents, their tools and their artifacts. Each agent runs in a lightweight WebAssembly VM that resumes mid-function after node restarts, rescaling, long waits and crashes. Tool calls run exactly once, under permissions agents can't bypass.
Golem runs every agent, every tool call and every artifact in its own WebAssembly VM. Every call out of a VM goes through Golem, which checks it against your permissions and records it, so the VM can resume anywhere.
WebAssembly, not Linux: each VM has private memory, files and environment, with no guest OS. Code can be tens of kilobytes; a VM starts in under 1 ms in the best case scenario.
A waiting VM is suspended, using no CPU or memory, for minutes or months. After a crash or node restart it's restored where it left off, without repeating completed calls.
A VM can't reach files, the network, secrets or other VMs directly. Every call out goes through Golem's permission checks, which no code, including AI-generated code, can bypass.
Each agent's VM keeps its whole execution durable.
export const SupportImpl = Support. implement({
init: () => ({ history: [] as Message[] }),
methods: {
async handle({ message, orderId }) {
this.history. push({ role: 'user', content: message })
// Golem lets lookupOrder reach only the orders API:
const reply = await llm. run( this.history, [lookupOrder])
if (reply.refundRequested) {
const approval = createWebhook()
await notifyReviewer(approval. getUrl(), reply)
// waits here for days if needed, using no CPU or memory:
const { approved } = ( await approval). json<Approval>()
// refunded once if the payment API honors idempotency keys:
if (approved) await refundOrder(orderId, reply.amount)
}
return reply.text
},
},
}) export const SupportImpl = Support. implement({
init: () => Effect. succeed({ history: [] as Message[] }),
methods: ( state) => ({
handle: ({ message, orderId }) => Effect. gen( function* () {
state.history. push({ role: "user", content: message })
// Golem lets lookupOrder reach only the orders API:
const reply = yield* llm. run(state.history, [lookupOrder])
if (reply.refundRequested) {
const approval = yield* Webhook.create
yield* notifyReviewer(approval.url, reply)
// waits here for days if needed, using no CPU or memory:
const payload = yield* approval.await
const { approved } = yield* payload. decode(Approval)
// refunded once if the payment API honors idempotency keys:
if (approved) yield* refundOrder(orderId, reply.amount)
}
return reply.text
}). pipe(Effect.orDie),
}),
}) #[agent_implementation]
impl Support for SupportImpl {
fn new() -> Self {
Self { history: vec![] }
}
async fn handle(
&mut self,
message: String,
order_id: String,
) -> String {
self. history. push( Message::user(message));
// Golem lets lookup_order reach only the orders API:
let reply = llm::run( &self. history, &[lookup_order]).await ;
if reply. refund_requested {
let approval = create_webhook() . unwrap();
notify_reviewer(approval . url(), &reply).await ;
// waits here for days if needed, using no CPU or memory:
let Approval { approved } = approval.await. json() . unwrap();
// refunded once if the payment API honors idempotency keys:
if approved {
refund_order(order_id, reply . amount).await ;
}
}
reply. text
}
} @agentImplementation()
final class SupportAgentImpl( id: String) extends SupportAgent:
private val history = ArrayBuffer.empty[ Message]
def handle( message: String, orderId: String): Future[String] =
history += Message( "user", message)
// Golem lets lookupOrder reach only the orders API:
llm.run(history.toList, List(lookupOrder)).flatMap : reply =>
if reply.refundRequested then refund(orderId, reply)
else Future.successful(reply.text)
private def refund( orderId: String, reply: Reply) =
val approval = HostApi.createWebhook()
for
_ <- notifyReviewer(approval.url, reply)
// waits here for days if needed, using no CPU or memory:
payload <- approval.await()
approved = payload.json[ Approval]().approved
// refunded once if the payment API honors idempotency keys:
_ <- if approved then refundOrder(orderId, reply.amount)
else Future.unit
yield reply.text fn SupportAgent::new() -> SupportAgent {
{ history: [] }
}
pub async fn SupportAgent::handle(
self : Self,
message : String,
order_id : String,
) -> String {
self.history. push({ role: "user", content: message })
// Golem lets lookup_order reach only the orders API:
let reply = llm. run(self.history, [lookup_order])
if reply.refund_requested {
let approval = @webhook. create()
notify_reviewer(approval. url(), reply)
// waits here for days if needed, using no CPU or memory:
let body = @json. parse(approval. wait(). text())
let { approved } : Approval = @json. from_json(body)
// refunded once if the payment API honors idempotency keys:
if approved { refund_order(order_id, reply.amount) }
}
reply.text
} type state struct{ history []Message }
var impl = Support. Implement( func( SupportID) *state {
return &state{}
})
var _ = impl. Handle(Handle, func(
ctx *golem. Context[state], in HandleIn,
) string {
s := ctx.State
s.history = append(s.history, Message{"user", in.Message})
// Golem lets lookupOrder reach only the orders API:
reply := llm. Run(s.history, lookupOrder)
if reply.RefundRequested {
approval := golem. NewWebhook[Approval]()
notifyReviewer(approval. URL(), reply)
// waits here for days if needed, using no CPU or memory:
decision := approval. MustAwait()
// refunded once if the payment API honors idempotency keys:
if decision.Approved {
refundOrder(in.OrderID, reply.Amount)
}
}
return reply.Text
}) After a node restart or crash, restoring a tool call's state replays its code using recorded results, without repeating completed effects.
export const refund = toolDefinition( 'refund')
. version( '1.0.0' )
. body(( body) =>
body
. positional( 'charge', z. string())
. positional( 'amount', z. number(). int())
. returns(z. object({ id: z. string(), status: z. string() })),
)
refund. implement({
// each call runs in a fresh WebAssembly VM:
refund: async ({ charge, amount }) => {
// Golem checks this request against the caller's permissions
// and adds an idempotency key, so a retry refunds once:
const res = await fetch( 'https://api.stripe.com/v1/refunds' , {
method: 'POST',
body: new URLSearchParams({ charge, amount: String(amount) }),
})
return ok( await res. json())
},
}) const Refund = Schema. Struct({
id: Schema.String,
status: Schema.String,
})
export const refund = Tool. toolDefinition( "refund", {
version: "1.0.0" ,
}). body(( body) =>
body
. positional( "charge", Schema.String)
. positional( "amount", Schema.Int)
. returns(Refund),
)
refund. implement({
// each call runs in a fresh WebAssembly VM:
refund: ({ charge, amount }) =>
// Golem checks this request against the caller's permissions
// and adds an idempotency key, so a retry refunds once:
pipe(
HttpClientRequest. post( "https://api.stripe.com/v1/refunds" ),
HttpClientRequest. bodyUrlParams({ charge, amount }),
HttpClient.execute,
Effect. flatMap(HttpClientResponse. schemaBodyJson(Refund)),
Effect. provide(FetchHttpClient.layer),
Effect.orDie,
),
}) #[derive( Schema, Deserialize)]
struct Receipt {
id: String,
status: String,
}
#[tool_definition(version = "1.0.0" )]
trait Refund {
async fn refund( &self, charge: String, amount: u64) -> Receipt;
}
struct RefundImpl;
#[tool_implementation]
impl Refund for RefundImpl {
// each call runs in a fresh WebAssembly VM:
async fn refund( &self, charge: String, amount: u64) -> Receipt {
// Golem checks this request against the caller's permissions
// and adds an idempotency key, so a retry refunds once:
let req = Request::post( "https://api.stripe.com/v1/refunds" )
. header( CONTENT_TYPE, "application/x-www-form-urlencoded")
. body( format!( "charge={charge}&amount={amount}"). into())
. unwrap();
let res = http::send(req) .await. unwrap();
http::response_json(res) .await. unwrap()
}
} final case class Refund( id: String, status: String) derives Schema
@toolDefinition(name = "refund", version = "1.0.0" )
trait RefundTool:
def refund( charge: String, amount: Int): Future[Refund]
@toolImplementation()
final class RefundToolImpl extends RefundTool:
// each call runs in a fresh WebAssembly VM:
def refund( charge: String, amount: Int): Future[Refund] =
val init = new RequestInit:
method = HttpMethod. POST
body = new URLSearchParams(js. Dictionary(
"charge" -> charge, "amount" -> amount.toString))
// Golem checks this request against the caller's permissions
// and adds an idempotency key, so a retry refunds once:
fetch( "https://api.stripe.com/v1/refunds" , init).toFuture
.flatMap(_.text().toFuture)
.map( Schema[Refund].jsonCodec.decode(_).toTry.get) #derive.tool ( "refund", version="1.0.0" )
struct Refund {}
#derive.golem_schema
pub(all) struct RefundResult {
id : String
status : String
} derive( FromJson)
// each call runs in a fresh WebAssembly VM:
#derive.arg ( "charge", scope="positional")
#derive.arg ( "amount", scope="positional")
pub async fn Refund::refund(
charge : String,
amount : Int,
) -> RefundResult {
let url = "https://api.stripe.com/v1/refunds"
let form = [( "charge", charge), ( "amount", amount. to_string())]
// Golem checks this request against the caller's permissions
// and adds an idempotency key, so a retry refunds once:
let res = @http. send( form_post(url, form)). unwrap()
@json. from_json( @json. parse( read_text(res)))
} type Refund struct{}
var Tool = tool. DefineTool[Refund]( "refund",
tool. Spec{Version: "1.0.0" })
type Args struct {
Charge string
Amount int64
}
type Result struct{ ID, Status string }
var Body = Tool. Body[Args, Result](
func( a *Args, s *tool. CommandSpec) {
s. Positional( &a.Charge)
s. Positional( &a.Amount)
})
// each call runs in a fresh WebAssembly VM:
var _ = Body. Handle( func( _ *tool. Context, a Args) ( Result, error) {
amount := strconv. FormatInt(a.Amount, 10)
// Golem checks this request against the caller's permissions
// and adds an idempotency key, so a retry refunds once:
res := golem. Must(http. PostForm(
"https://api.stripe.com/v1/refunds" ,
url. Values{"charge": {a.Charge}, "amount": {amount}}))
defer res.Body. Close()
var out Result
return out, json. NewDecoder(res.Body). Decode( &out)
}) Pages, files and live output, with each artifact in its own WebAssembly VM.
export const SiteAgent = defineAgent({
name: 'SiteAgent',
id: { project: z. string() },
// serve this agent's /public directory at /sites/{project}/:
http: http. mount( '/sites/{project}', {
exposeFiles: [{ route: '/*', path: '/public/$1' }],
}),
methods: { publish: method({ input: {}, returns: z. void() }) },
})
export const SiteAgentImpl = SiteAgent. implement({
init: () => ({}),
methods: {
publish() {
// copy in the site the agent built; its URLs serve it at once:
fs. cpSync( '/workspace/dist', '/public', { recursive: true })
},
},
}) export const SiteAgent = defineAgent({
name: "SiteAgent",
id: { project: Schema.String },
// serve this agent's /public directory at /sites/{project}/:
http: Http. mount( "/sites/{project}", {
exposeFiles: [{ route: "/*", path: "/public/$1" }],
}),
methods: { publish: method({ input: {}, success: Schema.Void }) },
})
const publish = () =>
Effect. sync(() => {
// copy in the site the agent built; its URLs serve it at once:
fs. cpSync( "/workspace/dist", "/public", { recursive: true })
})
export const SiteAgentImpl = SiteAgent. implement({
init: () => Effect.void,
methods: () => ({ publish }),
}) // serve this agent's /public directory at /sites/{project}/:
#[agent_definition(
mount = "/sites/{project}",
filesystem_bindings = [( "/*", "/public/$1")]
)]
pub trait SiteAgent {
fn new(project : String) -> Self;
fn publish( &self);
}
struct SiteAgentImpl;
#[agent_implementation]
impl SiteAgent for SiteAgentImpl {
fn new(_project : String) -> Self {
Self
}
fn publish( &self) {
// copy in the site the agent built; its URLs serve it at once:
copy_dir_all( "/workspace/dist", "/public"). unwrap();
}
} @agentDefinition(
// serve this agent's /public directory at /sites/{project}/:
mount = "/sites/{project}",
exposeFiles = Array(( "/*", "/public/$1"))
)
trait SiteAgent extends BaseAgent:
class Id( val project: String)
def publish() : Unit
@agentImplementation()
final class SiteAgentImpl( project: String) extends SiteAgent:
def publish() : Unit =
// copy in the site the agent built; its URLs serve it at once:
Fs.cpSync( "/workspace/dist", "/public",
js. Dynamic.literal(recursive = true)) #derive.agent
// serve this agent's /public directory at /sites/{project}/:
#derive.mount ( "/sites/{project}")
#derive.expose_files ( "/*", "/public/$1")
struct SiteAgent {
project : String
}
fn SiteAgent::new(project : String) -> SiteAgent {
SiteAgent::{ project }
}
pub async fn SiteAgent::publish(_self : Self) -> Unit {
// copy in the site the agent built; its URLs serve it at once:
copy_dir( "/workspace/dist", "/public")
} type SiteID struct{ Project string }
var SiteAgent = golem. DefineAgent[SiteID]( golem. Spec{
Name: "SiteAgent",
// serve this agent's /public directory at /sites/{project}/:
HTTP: &golem. Mount{
Path: "/sites/{project}",
ExposeFiles: []golem. FileMapping{
{Route: "/*", Path: "/public/$1"},
},
},
})
var Publish = SiteAgent. Method[golem. Unit, golem. Unit]( "publish")
type state struct{}
var impl = SiteAgent. Implement( func( SiteID) *state {
return &state{}
})
var _ = impl. Handle(Publish, func(
_ *golem. Context[state], _ golem. Unit,
) golem. Unit {
// copy in the site the agent built; its URLs serve it at once:
golem. Must0(os. CopyFS( "/public", os. DirFS( "/workspace/dist")))
return golem. Unit{}
}) Probably not. Each agent's whole execution is durable, and each agent has its own file system and can have its own database. Golem Social, a whole social network, runs on Golem with no external database. The same holds for your agents and tools, and for the artifacts your agents deploy. Keep existing databases where other systems share the data.
Yes. A framework shapes the agent loop and usually saves the conversation between turns. It runs inside a Golem agent, which keeps the agent's whole execution durable, in-progress tool calls included, and enforces permissions in the runtime.
Actor platforms persist each entity's state. Golem keeps the agent's whole execution durable, including its call stack, files and environment, so it continues in the middle of a call. Tool calls run exactly once, and the runtime enforces permissions.
Workflow engines usually ask you to split work into activities they record. In Golem, ordinary code is durable as it runs, with no restructuring, and each agent also gets its own VM and runtime-checked permissions.
Yes. Golem has a BSL license that converts to Apache 2.0, and is available for self-hosting for production workloads.
They stay on their current version until you update them. Golem checks that the new code is compatible before it moves a running agent over. Read the docs →
Install one binary and run the platform locally.