Skip to content

Latest commit

 

History

History
661 lines (486 loc) · 19.6 KB

File metadata and controls

661 lines (486 loc) · 19.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 things and containers.

Use who? for avatars only. Use occupants? for avatars and agents.

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
here?                  show where you are
claim                  claim an unowned room
:owner?                show the current room owner
owner did:ma:<target>  transfer the room to another bare DID through your avatar
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 north :message traveller You step through the green door.
:exit north :message blocked The green door is locked.
:exit north :message 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 lamp name Lantern set a visible or carried actor property through your avatar
name lamp as Lantern with nick lantern
                       set a visible or carried actor name and optional nick
describe lamp as A bright brass lantern.
                       set a visible or carried actor description

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.

If a newly created actor ends up with no owner and is not visible in your current room, claim it directly by DID-URL:

@runtime#thing:claim

In focus shorthand, the equivalent is:

claim did:ma:...#thing:

The trailing : marks the actor target token; any following argument is passed as the claim secret. The room command claim (without a target token) still claims the current room only.

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, dig north, prop lamp name Lantern, name lamp as Lantern, and describe lamp as ..., are avatar-mediated commands. The property commands resolve a carried or visible actor and forward its ordinary :prop method; that actor still enforces owner authority.
  • 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-init-prop! "name" "My Agent")
  (set-init-prop! "nick" "myagent")
  (set-init-prop! "description" "A custom movable agent.")
  (enter (string-append (ma-get-config-key "runtime") "#construct"))
  (ma-save-state!))

Why this works well:

  • set-init-prop! explicitly seeds initial state without publishing an intermediate parent-facing ctx. Use ordinary set-prop! after init so ctx attributes propagate to the current parent by default.

  • enter records the target as pending, sends room :enter, and commits the new parent only after a valid room :ctx ack.

  • :enter with the agent's node ctx informs the room how to present the occupant.

  • :move uses ordinary room exits. It asks the current room to choose one of the exits it knows with practical, non-security random choice. The room and exit use implementation-local movement traffic; the moving actor 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.

Pass ctx without workflow state

New actor-to-actor workflows should handle the current ctx and send the next ctx message directly. Do not persist a command, resolver result, transfer payload, or other intermediate step for later replay. Existing movement-specific state is not a pattern for parent/child, inventory, take, drop, or similar workflows; those flows use their explicit ctx handshakes.

Assume that any actor message or reply may have been lost. A caller may repeat a request because it cannot know what arrived. Handle every valid retry idempotently and return the same current authoritative ctx even when the requested parent or value is already committed. Do not answer retries with "already done", and do not add delivery logs, deduplication state, pending commands, or retry counters. Recompute the response from current actor state.

Resolve visible names, nicks, directions, and aliases only at the command boundary. After a successful lookup, discard the lookup term and use the canonical full actor DID or DID-URL for messages, authority checks, membership changes, cleanup, and persistent keys.

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-cmd-method! :duck
  (lambda (args msg)
    (ma-send! (msg-from msg)
      (list :print "A duck waddles through the room. It looks busy."))))

Use set-cmd-method! for command verbs that present something in-world, such as looking, speaking, emoting, moving, or noticing a local duck. Use set-rpc-method! for dry metadata/query/config verbs that should answer with ma-reply!. Actors also provide standard introspection such as :rpcs?, :cmds?, :api?, and :metas? for explicit runtime maintenance helpers.

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-cmd-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 in ordinary custom room code the method helper is usually set-cmd-method! or set-rpc-method!.

For tiny experiments, inline code also works:

dig west to Tiny Duck Room with (set-cmd-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/. Shared Scheme layers live in scheme-actor/:

actors/root.ma
actors/avatar.ma
actors/room.ma
actors/exit.ma
scheme-actor/actor.ma
scheme-actor/state.ma
scheme-actor/node.ma

Use /ma/node/0.0.1 as the base for a stateful actor that participates in the parent hierarchy. Parenting is not automatically physical containment; the concrete behaviour decides whether and how child ctx is displayed. Extend /ma/scheme/state/0.0.1 directly for a stateful utility actor that should stay outside the hierarchy.

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. Apply the complete tree when a new base kind such as /ma/node/0.0.1 and its derived kinds are introduced:

make kinds-cid

Apply it live from zion or another CRUD client:

@runtime/kinds: <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>