Skip to content

Latest commit

 

History

History
599 lines (436 loc) · 16.6 KB

File metadata and controls

599 lines (436 loc) · 16.6 KB

λ-間 HOWTO

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.

What you need

You need three things:

  • Kubo / IPFS stores actor source, Wasm, runtime manifests, DID documents, and room state as content-addressed IPFS/IPLD data.
  • ma runs 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:

Download ma:

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 the local build tools

Install Rust and the Wasm target. The λ-間 bootstrap includes a generic ma-scheme actor compiled to WebAssembly.

rustup target add wasm32-unknown-unknown

Make sure these commands exist:

ma --help
ipfs --version

If 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.

Generate lambda-ma.yaml

From the lambda-ma repository:

make clean
make

This does four things:

  1. Builds scheme-actor/actor.wasm.
  2. Publishes scheme-actor/actor.wasm to IPFS.
  3. Publishes scheme-actor/stdlib.ma and actors/*.ma to IPFS.
  4. Writes the generated bootstrap file to dist/lambda-ma.yaml.

Check that the generated YAML has no unresolved placeholders:

make check

You can inspect the generated file:

less dist/lambda-ma.yaml

Do not edit dist/lambda-ma.yaml by hand. Edit lambda-ma.template.yaml, actors/*.ma, or scheme-actor/, then run make again.

Option A: bootstrap and start ma directly

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.yaml

The flag is --bootstrap, not --bootstrapo.

When ma is running, open zion from the local runtime:

http://localhost:5003/zion

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

Option B: generate a reusable root CID

Use this when you want a stable starting point that can be reused without copying the YAML file around.

make root-cid

This is a convenience wrapper around:

ma --gen-root-cid dist/lambda-ma.yaml

It 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...

Enter and change your first room

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 as rms after 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, and dig north, are avatar-mediated avatar-mediated commands. prop is 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.

Good practice for movable agent init

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:

  • enter records the target as pending, sends room :enter, and commits the new parent only after a valid room :ctx ack.
  • :enter with agent-ctx informs the room how to present the occupant.
  • :move and :go use ordinary room exits. :go <direction> names an exit; :move asks 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 :ctx to the agent. The agent then performs the target-room :enter itself, 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.

Add custom code to one actor

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.

Modify λ-間 itself

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 check

Then choose a bootstrap workflow again:

ma --bootstrap dist/lambda-ma.yaml

or:

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-cid

Apply 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>

Publish and share your variant

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-cid

Troubleshooting

ipfs: command not found

Install Kubo from https://docs.ipfs.tech/install/command-line/ or make sure IPFS Desktop's ipfs binary is on your PATH.

kubo RPC is not reachable for bootstrap

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.

make fails while publishing CIDs

Check that Kubo is running:

ipfs id

If ipfs id fails, fix Kubo first. λ-間 cannot generate a complete bootstrap without publishing actor files to IPFS.

zion enters but room commands fail

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.

I changed actors but the world looks unchanged

Regenerate and re-bootstrap:

make clean
make
ma --bootstrap dist/lambda-ma.yaml

If 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>