This guide starts from a fresh machine and ends with a running λ-間 world that you can enter from zion, edit, fork, and reset.
λ-間 is a seed world for ma-runtime. It is similar in spirit to
lambdamoo-core: a small functional base that gives a new world rooms, exits,
avatars, and a root actor. You are expected to change it. The shipped world is
not sacred; it is a starting point for your own runtime.
For normative world protocol behaviour (routing split, room/enter contract, actor verbs, ownership and parent-authority rules), use REFERENCE.md.
You need three things:
- Kubo / IPFS stores actor source, Wasm, runtime manifests, DID documents, and room state as content-addressed IPFS/IPLD data.
maruns your actor runtime, loads the λ-間 bootstrap, and serves zion locally.- λ-間 is the seed actor set and bootstrap YAML for a simple programmable world.
Download Kubo/IPFS:
- IPFS Desktop, easiest for new principals: https://docs.ipfs.tech/install/ipfs-desktop/
- Kubo command-line install: https://docs.ipfs.tech/install/command-line/
Download ma:
ma-runtimereleases: https://github.com/bahner/ma-runtime/releases
Kubo must be running before you build or bootstrap λ-間. The make process uses
ipfs add to publish actor files and Wasm to your local node. The ma runtime
also uses Kubo to publish the runtime manifest and DID documents. If Kubo is not
running, the build may fail, and ma --bootstrap / ma --gen-root-cid cannot
publish the world.
Install Rust and the Wasm target. The λ-間 bootstrap includes a generic ma-scheme actor compiled to WebAssembly.
rustup target add wasm32-unknown-unknownMake sure these commands exist:
ma --help
ipfs --versionIf you installed IPFS Desktop, the ipfs command may already be available. If
not, install Kubo from the command-line link above or add the bundled ipfs
binary to your PATH.
From the lambda-ma repository:
make clean
makeThis does four things:
- Builds
scheme-actor/actor.wasm. - Publishes
scheme-actor/actor.wasmto IPFS. - Publishes
scheme-actor/stdlib.maandactors/*.mato IPFS. - Writes the generated bootstrap file to
dist/lambda-ma.yaml.
Check that the generated YAML has no unresolved placeholders:
make checkYou can inspect the generated file:
less dist/lambda-ma.yamlDo not edit dist/lambda-ma.yaml by hand. Edit lambda-ma.template.yaml,
actors/*.ma, or scheme-actor/, then run make again.
This is the shortest path when you just want to run the world now:
ma --bootstrap dist/lambda-ma.yaml--bootstrap publishes the manifest described by dist/lambda-ma.yaml, uses
the resulting root CID for this runtime start, then continues running ma.
If you copy dist/lambda-ma.yaml somewhere else, pass that path instead:
ma --bootstrap lambda-ma.yamlThe flag is --bootstrap, not --bootstrapo.
When ma is running, open zion from the local runtime:
Create or unlock your zion identity, then claim your local runtime if you have not already done so:
.my.ma:claim
Then enter the λ-間 world:
.ma!discover
@ma/config/root: @ma#root
.enter @ma
Use this when you want a stable starting point that can be reused without copying the YAML file around.
make root-cidThis is a convenience wrapper around:
ma --gen-root-cid dist/lambda-ma.yamlIt publishes the λ-間 bootstrap to IPFS, prints one CID, and exits. That CID is the root of the generated runtime manifest.
Example:
bafyreib...
Start ma from that root CID:
ma --root-cid bafyreib...This resets the runtime head to that world root for the start. The runtime then
persists the selected root in its config as it runs. If you later want to reset
back to the same fresh λ-間 starting point, stop ma and start it again with the
same root CID:
ma --root-cid bafyreib...This is useful for experiments. You can build rooms, break things, learn, and then return to a known clean seed world.
Keep the printed root CID somewhere convenient, for example in a notes file:
lambda-ma fresh root: bafyreib...
After zion has entered the world, you are in #construct, the initial room.
Start with:
look
help
help here
Claim the room so you can edit it:
claim
Set a name and description. Colon-prefixed commands go directly to the focused room instead of through your avatar:
:prop name The Workshop
:prop description A bright room with cables on the floor and a half-built door in the north wall.
look
Create a new room to the north:
dig north to The Gallery
go north
look
The new room is owned by you automatically. Change it too:
:prop description A long quiet gallery. Every wall is waiting for a better idea.
Move back:
go south
Quick regression check for room presence rendering:
look
Expected shape in output:
- An
Occupants:line that includes live room occupants, including avatar presence and agents such asrmsafter those agents have entered the room. - A separate
Things:line for room-local non-avatar aliases.
Use who? when you specifically mean people/avatar presence. Use occupants?
when you want the broader room occupant list.
If look only shows Things: none. or misses expected occupants while actors
are clearly active in the room, the room behaviour is stale. Rebuild and
re-bootstrap (see troubleshooting below).
Useful room-building commands:
help show avatar/avatar-mediated commands
help here show what this room says is possible
claim claim an unowned room
owner show the current room owner
owner did:ma:<target> transfer the room to another bare DID
make thing <.my.things.lamp
create a thing through your avatar; the init string sets owner and location
did? [kind] lamp show the DID for a visible occupant, thing, or exit
owner? lamp show who owns a visible occupant, thing, or exit
dig north to Name create an exit and a new room
fill north remove the north exit
look north inspect the north exit
lock north lock the north exit
unlock north unlock the north exit
exit-message north traveller You step through the green door.
exit-message north blocked The green door is locked.
exit-message north source Someone slips through the green door.
:dids? owner lists visible occupants, things, and exits with DIDs
:remove Alice owner removes Alice if that nick is unique here
:remove did:ma:...#x owner removes an exact occupant DID-URL
:prop name Name set the focused room name
:prop description ... set the focused room description
:prop description reset the focused room description
prop name Name shorthand for setting the focused room name
make is avatar-owned. Zion only expands the command text, and the current room
is not the factory. The init text is the new actor's creation payload; set
owner and parent there. Use parent equal to the avatar DID-URL when the
avatar is holding the thing, or the room DID-URL when it should appear in the
room immediately. A convenient flow is to edit the init text with
.my.things.lamp!edit, then run make thing <.my.things.lamp.
Digging an existing direction replaces that exit. Filling a direction removes
the exit from the current room and asks the exit actor to terminate itself; it
does not delete the target room. Exits are inspectable objects: look north
shows the exit name, description, owner, source room, target room, direction,
and lock state. Locked exits stay visible, but go north prints the blocked
message instead of traversing. To link to an existing room instead of creating a
new one, use a room DID-URL:
dig mirror to @sky#FQWJA5V3
Runtime-local fragments may exist in old state, but actor messages use full DID
or DID-URL values. A full did:ma:...#room target may point at another runtime.
The target room must run compatible room code and confirm that you own it before
the source room creates the exit. The source room first sends :ping to the target
room; after :pong, it asks for ownership authorisation.
The important rule is simple:
- Commands without a leading colon, such as
look,go north,say hello, anddig north, are avatar-mediated avatar-mediated commands.propis a room-metadata shorthand and targets the focused room directly. - Commands with a leading colon, such as
:prop name ...and:help, are sent directly to the focused room.
When you create your own /ma/scheme/agent/0.0.1 entity, keep init explicit in
the creation-time init code you send when creating the entity (init: in
bootstrap YAML, or inline with (...) code for creation flows). Do not rely on
hidden helper init functions.
Recommended pattern:
(begin
(set-prop! "name" "My Agent")
(set-prop! "nick" "myagent")
(set-prop! "description" "A custom movable agent.")
(enter (string-append (ma-get-config-key "runtime") "#construct"))
(ma-save-state!))Why this works well:
enterrecords the target as pending, sends room:enter, and commits the newparentonly after a valid room:ctxack.:enterwithagent-ctxinforms the room how to present the occupant.:moveand:gouse ordinary room exits.:go <direction>names an exit;:moveasks the current room to choose one of the exits it knows with practical, non-security random choice. The room sends movement ctx to the exit, the exit returns transformed ctx to the room, and the room sends:ctxto the agent. The agent then performs the target-room:enteritself, so the target room remains responsible for admission.- Full init logic stays in the code principals provide at creation time, so principals can
adjust it safely when creating entities.
Agents can use exits, but they do not expose
:dig.
Sometimes you want one room to have a special method or two without changing
actors/room.ma, without using CRUD, and without registering a new kind. λ-間
supports that for any Scheme-backed actor; the example below customises a room
from inside that room.
The normal room behaviour still comes from /ma/room/0.0.1. Your extra code is
added only to that room.
Go to the room, claim it if you have not already, and open the behaviour editor:
claim
:behaviour!edit
That opens the room's current per-entity behaviour source if one is already set,
or a blank editor if the room has no custom code yet. Publishing requires the
actor's owner prop to match you. Add Scheme code and press Publish.
For example:
(set-method! :duck
(lambda (args msg)
(ma-send! (msg-from msg)
(list :print "A duck waddles through the room. It looks busy."))))Zion publishes the editor contents as text/plain, sends the returned
/ipfs/<cid> to the actor's :behaviour method, and the runtime reloads only
that actor. Call the new method directly:
:duck
This does not create a new kind. The room is still a normal λ-間 room with
look, say, go, claim, owner, dig, :prop, and the rest of the room
behaviour. The /ipfs/<cid> is just an extra behaviour layer for that one room,
loaded after the base room code.
Because that extra layer is evaluated after the base room code, it can extend
the room's own help text without changing actors/room.ma. Save the original
room-help-text, redefine it, and keep using the existing :help method:
(define base-room-help-text room-help-text)
(define (room-help-text)
(string-append
(base-room-help-text)
"\n :duck notice the local duck"))Now both :help and help here include the local room affordance.
To see the current per-actor behaviour reference:
:behaviour
You can do the same thing manually: write a duck.ma file, publish it with
ipfs add --quieter duck.ma, then run :behaviour /ipfs/<cid> from the focused
actor.
The editor command is just the friendly path over that same mechanism.
You can also compose code files. If duck-room.ma contains this:
(ma-include-ipfs #/ipfs/bafkreiduck...)
(set-method! :pond
(lambda (args msg)
(ma-send! (msg-from msg)
(list :print "The pond is still. Something under it is considering you."))))then publish duck-room.ma and use its CID:
ipfs add --quieter duck-room.ma:behaviour /ipfs/<duck-room-cid>
The include form is named ma-include-ipfs, and the method helper is named
set-method!.
For tiny experiments, inline code also works:
dig west to Tiny Duck Room with (set-method! :duck (lambda (args msg) (ma-send! (msg-from msg) (list :print "quack"))))
Inline with (...) code is delivered as creation-time init code. It is useful
for quick live experiments, but for room code you want to keep, publish a file
and use :behaviour /ipfs/<cid> after the room exists.
You can also attach code while digging a new room:
dig east to Duck Room with /ipfs/bafkreiduck...
go east
:duck
That stores the /ipfs/<cid> as the new room's behaviour reference immediately.
One important limitation: with ... only applies when a new room is created. If
the target after to is an existing room, λ-間 links to that room and ignores
custom code. To add code to an already-existing room, enter it and use
:behaviour!edit.
The kind definitions live in kinds/, and the actor behaviours live in
actors/:
actors/root.ma
actors/avatar.ma
actors/room.ma
actors/exit.ma
To change the default room behaviour, edit actors/room.ma. To change movement
or avatar-mediated commands, start with actors/avatar.ma and actors/room.ma. To change
world entry and zion context, start with actors/root.ma.
To change a kind descriptor, edit the matching kinds/*.yaml file. make
generates the runtime.kinds bootstrap map from those files; keep example
descriptors with placeholder CIDs out of BOOTSTRAP_KIND_FILES until they are
ready to publish.
After editing, regenerate and test the bootstrap:
make clean
make
make checkThen choose a bootstrap workflow again:
ma --bootstrap dist/lambda-ma.yamlor:
make root-cid
ma --root-cid <printed-cid>If you are only updating kinds or behaviours for an existing runtime, generate and apply a kinds CID instead. This patches the runtime's kind registry without replacing existing entities or their state:
make kinds-cidApply it live from zion or another CRUD client:
@runtime/kinds: /ipfs/<printed-kinds-cid>
or apply it on the next daemon start:
ma --kinds-cid <printed-kinds-cid>The source files are the important artefacts:
lambda-ma.template.yaml
actors/*.ma
scheme-actor/src/*.rs
scheme-actor/stdlib.ma
Makefile
Do not commit generated output such as dist/, scheme-actor/target/, or
scheme-actor/actor.wasm.
To share a reproducible seed world, share the source branch and the generated
root CID. Someone with Kubo and ma can start from that CID directly:
ma --root-cid <your-root-cid>To let them rebuild it themselves, tell them to run:
make
make root-cidInstall Kubo from https://docs.ipfs.tech/install/command-line/ or make sure
IPFS Desktop's ipfs binary is on your PATH.
Start IPFS Desktop or the Kubo daemon, then try again. ma needs the local Kubo
RPC API, normally at http://127.0.0.1:5001, to publish the bootstrap manifest.
Check that Kubo is running:
ipfs idIf ipfs id fails, fix Kubo first. λ-間 cannot generate a complete bootstrap
without publishing actor files to IPFS.
Run:
.ma!discover
@ma/config/root: @ma#root
.enter @ma
The /config/root value tells zion which runtime actor is the world root. For
λ-間 it should be @ma#root.
Regenerate and re-bootstrap:
make clean
make
ma --bootstrap dist/lambda-ma.yamlIf you are using the root CID workflow, remember that old CIDs are immutable. After source changes, generate a new root CID:
make root-cid
ma --root-cid <new-cid>