Summary
Software that can’t leave the machine it was born on isn’t really yours. Version 1.7 of the pico engine lets a pico and its subtree move between engines while its relationships keep working, because other picos follow its DID rather than its address. This post describes what moves, why identity is what makes it possible, and what’s still ahead.
In one of my favorite books, Vernor Vinge's Rainbows End, Juan Orozco and a pair of troublesome twins play in a public park where the physical world is overlaid with a constantly shifting digital one. Creatures, games, and other virtual constructs inhabit the park, interacting with people and with the networked infrastructure around them. What caught my attention in that scene is that these software entities have an identity independent of any particular piece of infrastructure while still maintaining a relationship with the physical and network environment they inhabit. They exist in the world without belonging to a particular computer.
I think the scene sticks with me because that idea has long been my dream for picos. A pico has an identity, persistent state, rules, and relationships, but none of those things inherently require it to remain on the machine where it was created. If we separate the identity of a pico from its execution location, a pico could move from one host to another while remaining the same computational entity. Other picos and applications could keep interacting with it without needing to know where it's running.
Why would we want a pico to move? There are plenty of reasons:
- Resilience and censorship: Move away from a host that becomes unavailable, hostile, or filtered.
- Economics: Migrate to cheaper compute or storage when there's no reason to stay put.
- Latency: Move computation closer to the people, devices, or other picos it interacts with.
- Bandwidth: Move toward a data source rather than continually hauling data across the network.
- Privacy and jurisdiction: Keep a pico on infrastructure with appropriate governance or data-handling rules.
- Autonomy: Perhaps most interestingly, let the pico itself decide when and where to move.
Underneath all of those is a more powerful reason. If your picos can only live on one engine, you don't own them in any meaningful sense; you're renting a seat from whoever runs that engine. Portability is how we avoid lock-in, and avoiding lock-in is a precondition for personal agency in a system built from software that acts on your behalf. Version 1.7 of the pico engine, released a few days ago, takes the first real step: a pico and its subtree can move from one mesh to another, on a different engine, while the relationships it has with other picos keep working.
A Bike Changes Hands
The easiest way to see what this means is to watch it happen. The scenario in the demo video shown below imagines a bike club that owns gravel bikes for members to borrow and runs a community pico to coordinate rides. Each bike has a pico that serves as its digital twin: the pico holds the bike's state and history, has a child pico for the bike's computer, and has relationships with the club's Manifold app and with the Trail Ride Community. A ride coordinator with his own mesh has formed a relationship with Gravel Bike #7 so it can take part in the rides he organizes. Then the club sells bike #7, and the bike's pico needs to move to the new owner's mesh on a different engine.
The bike club and Alice, the new owner, use Manifold, the framework for managing things and communities that I've written about before and recently moved my own sensor network onto. Manifold matters here for a reason I'll come back to: a thing pico assumes certain services exist above it in the tree, and the destination has to provide them. What you see in the video is the engine's developer interface rather than anything a bike owner would use; it lets us observe and manipulate the picos directly, which is what a demo of the plumbing needs. Three meshes run on two engines, the club's and the coordinator's on one and the new owner's on the other.
The bike's pico and its bike-computer child are exported from the club's mesh as an encrypted blob and imported under the new owner's Manifold. After the import, the bike has the same did:webvh identity it had before, the ride coordinator's relationship with it still works, and the Trail Ride Community back on the club's engine still has a link to it. The new owner's Manifold forms a fresh relationship with the bike, since that one belongs to the new context rather than the old. Nothing had to be reintroduced.
Digital twins are where durable identity earns its keep. A pico that stands in for a physical thing grows more valuable the longer it lives, because it accumulates the thing's history, relationships, and state over years. I made this argument in August using an example from Fuse, where selling a car meant handing its pico to the new owner with the maintenance history intact. The bike is the same idea. Its identity is bound to the bike, not to the club, and it has to be able to outlive any particular owner or engine because the bike will.
What Makes a Pico Portable?
Making a pico portable requires separating what the pico is from where the pico runs. A pico isn't a process, container, or virtual machine; those are execution mechanisms tied to particular infrastructure. A pico is an enduring computational entity with an identity, state, rules, and relationships. The host pico engine is simply the place where that entity happens to execute.
That distinction defines what has to move. A portable pico needs all the information necessary to recreate itself on a new engine: its rulesets, persistent state, configuration, keys, and information about its relationships with other picos and services. Some of that information needs special handling. Private keys, for example, shouldn't be copied around indiscriminately. But conceptually the goal is clear: capture the pico's computational state, transfer it to another compatible host, resume execution there, and retire the old instance.
Identity continuity poses the hard problem. If a pico moves, everyone it interacts with shouldn't have to get reacquainted with it; its identity must remain stable even when its location changes. That means addressing and routing have to be indirect: send an event to the pico, not to the server currently running it. Some discovery mechanism maps the pico's enduring identity to its current network location, and moving becomes a routing update rather than an identity change.
Relationships create a similar requirement. A pico might have channels to dozens of other picos in different meshes and even on different engines, and those relationships should survive migration without being recreated. Ideally, the endpoints of a relationship identify the participating picos rather than their hosts, so that after a move messages simply begin flowing to the pico's new location. Webhooks don't enjoy this property; each one holds a URL, and every one of them has to be updated by hand when the thing it points at moves. This is the difference between having a protocol and merely having a convention.
One thing I learned building this is that the unit of portability isn't a single pico and it isn't a whole mesh either. Exporting an entire mesh is the wrong granularity; the bike club isn't moving, only the bike is. But a pico rarely travels alone. Bike #7 has a bike-computer child, and parent-child relationships are part of how a pico behaves: children rely on their parent for rulesets, shared relationships, and policy. So what moves is a subtree, the pico and its descendants, and the importer attaches it under a new parent in the destination mesh.
That subtree boundary creates a dependency the design has to take into account. A thing pico inside the Manifold framework assumes Manifold's services exist above it; importing it under a bare root on another engine would leave it running but functionally broken. The export carries the subtree only, not its ancestors, so the importer is responsible for supplying the platform the subtree expects. In the demo that means adopting the bike under the new owner's Manifold app pico, which is exactly where a thing pico belongs.
Those pieces are what 1.7 puts in place, and they're the foundation for the capability imagined in the scene from Rainbow's End I mentioned earlier. With a stable identity, relationships that follow it, and an export that captures what it is, migration could become part of a pico's own behavior in a future release. A pico could monitor the environment in which it runs, discover alternative hosts, evaluate them against its own policies, and decide whether moving is advantageous, weighing cost, latency, bandwidth, reliability, jurisdiction, trust, or the availability of particular services. None of that exists today, but nothing in the architecture stands in its way now. At that point portability stops being a deployment feature; just as a pico can react to an event by updating state or sending a message, it could react by moving itself. The infrastructure provides places to run; the pico decides where it wants to live.
Why DIDs Make the Difference
When I added pico-to-pico identity in version 1.6, I said it was the foundation portability was waiting on. That wasn't a side benefit; I built the layer for this express purpose. Before 1.6, a pico's only identifiers were its channel identifiers (ECIs), and an ECI is both a local routing address and, in practice, a capability. Change the ECI and you break every peer, UI link, and webhook that holds the old value. Moving a pico while keeping its ECIs is fraught from both a security and an operability standpoint, and moving it without them meant starting over.
Giving every pico two kinds of DIDs splits those concerns apart. The did:webvh is the pico's stable, long-term address, the passport it hands to strangers. Its self-certifying identifier (SCID) is fixed for the life of the DID, while the host and path in the DID's log can change; a move appends an entry to the log saying where the pico now lives, and anyone who resolves the DID finds the new location. The did:peer pairs that each relationship uses are private, point-to-point identifiers with no engine address baked into them. Peers follow the DID, and the engine mints fresh ECIs on the new host as nothing more than routing handles.
Relationships survive because DIDComm already knows how to handle a party whose endpoint or keys change. After an import, the moved pico sends a rotation message to each of its peers, and each peer updates where it sends traffic for that relationship. The ride coordinator's mesh never learned the bike's new ECI and never needed to; it learned that the DID it already trusted now resolves somewhere else. That's an example of what it means for identity to be a protocol rather than a convention: the update is a message the relationship was designed to carry, not a chore for the operator.
What Actually Happens in a Move
The export walks the subtree and packages everything needed to reconstitute it: installed rulesets, entity state for each ruleset, channels with their tags and policies, scheduled events, and an identity snapshot for each pico that includes its did:webvh log and keys and its peer relationships. Rulesets with a canonical remote URL are carried by reference and fetched again on import; only rulesets that can't be retrieved on the destination get embedded inline. The whole package is encrypted with a secret the exporter chooses, because a blob that contains keys and relationship state is a capability-bearing secret and has to be handled like one. Transferring the blob is up to you; nothing in Wrangler moves it for you yet.
The import creates the picos under the recipent parent, mints new ECIs for every channel, and hydrates entity state, remapping any old ECIs that rulesets had stored in their own state. Then it raises wrangler:pico_imported so rulesets can run their own migration logic. With the picos in place, the identity service relocates each did:webvh by appending the new location to its log and notifies every peer of the move over DIDComm. Picos on the destination engine that had relationships with the subtree before it arrived get reconciled locally, so a relationship formed before the move keeps working after it.
What 1.7 implements is more accurately a copy than a move, and the distinction matters. The import does nothing to the source; the donor subtree is left behind in a vestigial state, still holding the same DID, and has to be deleted once peers have cut over. Leaving it there is worse than untidy, since an engine that still has a local pico with that DID will route to the stale copy rather than the real one. Atomicity, rollback when a move half-completes, and forwarding from the old location are all open problems. The design notes in the repository lay out what's done and what isn't.
Release 1.7
Version 1.7.0 of the pico engine ships two things. The first is the portable move described above. Underneath are engine primitives: ctx:exportRulesetState and ctx:importRulesetState for a single ruleset's state, and ctx:exportSubtree and ctx:importSubtree for a whole subtree. The developer's interface to that functionality is io.picolabs.portable_subtree, part of Wrangler, which wraps the full pipeline, including DID relocation and peer notification, in a single query to export and a single event to import. On the Manifold side, io.picolabs.manifold_portable_thing in the manifold-api repository builds on that interface to export a thing from one Manifold and import it, with registration, into another. The repository includes a docker-compose file that stands up two engines so you can run the cross-engine demo yourself.
The second is an upgrade to the peer DIDs themselves. Relationships formed on 1.7 use did:peer:4, the variant on DIF's standardization path, in place of the hand-rolled did:peer:2 identifiers 1.6 used. No npm package existed, so the engine carries a TypeScript port of DIF's reference implementation that I intend to contribute upstream. Existing relationships keep their did:peer:2 identifiers and keep working; nothing converts automatically, and I'd rather let old relationships age out than rewrite identifiers that other picos already hold.
Just a Start
What I ultimately want is for a pico to move itself on command, and eventually on its own judgment, with the old instance retired as part of the same operation. This release gives us the export, the import, and the identity machinery that lets relationships follow the pico, but it leaves the transfer, the cleanup, and the decision to a human at a developer console. Those are the pieces that turn a copy into a move, and they're what come next.
I also want to be careful about what one working demo proves. We implemented and tested this against a scenario we designed, and a scenario you design tends to contain the cases you thought of. There are almost certainly dependencies between picos, relationships that cross subtree boundaries, and external systems holding old addresses that the bike club never exercised. The only way to find them is to move real picos between real meshes, which is why I expect the next few releases to be shaped by what breaks.
The important lesson is that supporting personal agency in a technical system is a choice, and it's almost always the harder one. Pinning a pico to the engine where it was born is easy; making its identity survive a move took an identity layer, a messaging protocol, and a careful accounting of what a pico actually is. We can build systems where the things that act for people can be picked up and moved when the people decide to, or we can build systems that are simpler because they quietly make that decision for them. The choice is ours, and this release is my vote.
Photo Credit: A pico packs up and moves to a new engine from ChatGPT (public domain)





