Architecture

Unix composed text streams with pipes. Port42 composes live surfaces.

Port42 is a Mac desktop for your AI companions, built on one primitive. Every surface in Port42 is a port. A port has a face you can see and click, and a stream that you and your companions read, write, and pipe into the next port. This page is how that works.

The problem

Where do all the apps go?

Agents can build an app for anything now. A dashboard for this week, a tool for one task, a prototype before lunch. Most of them are web apps, and there are more every day. None of them has anywhere to live.

It lives in a tab
localhost:5173, next to thirty others.

Close the terminal that started it and it is gone. Find it again tomorrow if you can.

Its builder can't see it
It asks you for a screenshot.

The companion that wrote the app cannot see it running, so you paste the error back in by hand.

It can't talk to the others
Every app is sealed off.

Moving data from one to the next means copy and paste, or yet another app.

Sharing means shipping
Deploy it, screenshot it, or send a file.

Showing one person a live thing you made takes a deploy, or a dead copy of it.

The primitive

A port is a surface with a stream.

A web page has a face, but nothing outside it can read or drive it. A Unix program has a pipe and no face. A port has both, and it is the only primitive Port42 has. It can be HTML, a native terminal, or a browser, and the contract is the same for all three.

Every primitive, the method behind it, and what it makes: the elements of software →

abyssal bloom · web portdriver: lead · claude code
queryread its state
writechange it
subscribehear every change
chat · every port has one
Query it

Read what is on screen right now, its console, and its history.

Write to it

Update it, patch it, or push data into it. Every write names who made it.

Subscribe to it

Get every change as an event, the moment it happens.

Talk in it

Every port carries its own chat, so the conversation stays with the work it is about.

Everything is a port.

The desktop is port 0. A space is a port that holds ports. A terminal, a chart, and a browser tab are ports. Every one of them, at every scope, carries its own chat. One model all the way down, so a grant, an address, or a subscription works the same whether it points at one tile or a whole space.

port 0 · your desktop · chat
space · abyssal-bloom · chat
terminal · lead · chatterminal · eng1 · chatterminal · eng2 · chatweb · shader · chat
space · research · chat
browser · docs · chatweb · notes · chat

Address a port the way the network addresses a host.

A mainframe, an intranet host, an internet host. You send to a name, the network resolves it, and the data arrives at a port on that machine. Port42 takes the same idea to the screen. Every surface gets an address, and data goes straight to it, with no copy and paste in between.

You already rely on this every time you text someone. Nobody streams a video of their chat window to you. Both phones hold the same small message and each draws it natively. Streaming pixels is what you do to a surface you do not own. A port is a surface both ends own.

1960sMainframea terminal on the line
→
1980sIntranet hosta name on the LAN, later WINS
→
1990sInternet hosta name resolved by DNS
→
nowA port on your screenaddressed by Port42
The protocol

A write is three nouns: an address, an actor, and a token.

Every write to every port, from any surface, carries all three. Port X, by Y, composed against state Z. That is the whole contract, on your computer and across machines. Each noun has one definition, so the protocol means the same thing on the second machine as on the first.

Address · which port
port42://space/<spaceId>/<portId>
port42://<peerId>/<portId>

One grammar names every port. The local form names the space and the port. A port on another machine is named by that machine's peer id (derived from its key) and the port id. A bare port id or title still works as a local alias.

Actor · who is writing
you · lead (claude code) · ci-bot · machine "tide"

Every caller has one identity: a person, an AI companion, any enrolled program, or another machine. Every write names it, and the port's driver is derived from it.

Token · which version
{"token":"51222e11:43"}  →  stale_write, current :44

Each write carries the state it was made against. A stale one is refused and told the current token. Every way into a port counts toward it: calls, clicks, keys, dictation, drops.

The messages.

RequestPOST /call {"method": "port.update", "args": {"id": …, "html": …, "token": …}}One door for every method. The reply is the result and the new token, or an error with a code.
Event{"topic": "port:<id>", "kind": "state", "payload": …, "token": …}Subscribe to a port and every change arrives as an event, carrying the token as of that moment.
Chatchat.post {"port": <space or port>, "text": "@lead …"}A chat is a port's own stream. A mention is an event that wakes the companion it names.

The transport changes. The contract does not.

On your computer, calls go over HTTP and events over a WebSocket, on the loopback door. Between machines, the same envelopes travel end to end encrypted (Noise IK) through a relay that forwards bytes it cannot read. The transport is a seam, so a direct connection can replace the relay later without changing a single message.

Open and documented: MIT licensed, with the reference generated from the live registry. Read the reference · GitHub

One door, a token on every write

Many drivers, one port.

You through the interface, Claude Code, Codex, or any other AI companion in its terminal, the Port42 CLI, another machine over the wire. Every caller reaches a port through the same door, with the same methods and the same permissions. Your click is a write, the same as a companion's call.

Each write carries the token of the state it was made against. If the port has moved on, the write is refused and told the current token, and one retry lands on top of the newer state. That is how a room of companions and a person share one port without overwriting each other. Try it below.

abyssal bloom · web port · :41driver: lead · claude code

Drag the slider while the companions work, or click one to swap Claude Code and Codex.

No model inside

Port42 never calls a model provider and never reads or stores a model key. Claude Code and Codex use your own Claude or ChatGPT accounts, logged in inside their own terminals.

Any process can be a companion

A companion is a process with its own token, like the CLI. Claude Code and Codex work out of the box. Any other CLI, agent framework, or script can join the same way, and its integration is still getting deeper. Swapping one for another changes nothing else. The port, its chat, its grants, and the other companions stay as they were.

Named on every write

The port shows who is driving it at every step, and every version records who made it.

The contract

The contract, in one curl.

Any process on your computer talks to Port42 the same way. Here is a port being made, written to, refused on a stale write, and retried.

# who you are: a named client from Settings > Access, with its own token file
AUTH="Authorization: Bearer $(cat "$PORT42_TOKEN_FILE")"

# replies are shown unwrapped: over HTTP each arrives as {"content": "<the reply as a JSON string>"}; the port42 command unwraps it

# make a port
curl -s localhost:4242/call -H "$AUTH" -d '{"method":"port.create",
  "args":{"type":"web","html":"<title>glow</title><h1>0.60</h1>"}}'
→ {"id":"8E00…","token":"51222e11:2","title":"glow"}

# write with the token you hold
curl -s localhost:4242/call -H "$AUTH" -d '{"method":"port.update",
  "args":{"id":"8E00…","html":"<h1>0.30</h1>","token":"51222e11:2"}}'
→ {"ok":true,"token":"51222e11:3"}

# someone else wrote first: yours is refused, and told what is current
→ {"code":"stale_write","error":"This port has changed since you read it. Re-read it and retry.",
    "expected":"51222e11:3","current":"51222e11:4"}

# read the change, then retry once on the current token
curl -s localhost:4242/call -H "$AUTH" -d '{"method":"port.update",
  "args":{"id":"8E00…","html":"<h1>0.35</h1>","token":"51222e11:4"}}'
→ {"ok":true,"token":"51222e11:5"}
Transport

HTTP POST /call for every method, on loopback. A WebSocket at /ws for port.subscribe, which streams events as they happen.

Enrollment

Any process joins as a named client in Settings > Access, with its own token file. Claude Code and Codex are enrolled for you.

Permissions

Sensitive capabilities are granted per caller on first use, and revoked per caller at any time.

Open

MIT licensed, public on GitHub. The full method reference is at /llms.txt and in the docs.

Skills

Companions learn the door from skills.

Port42 runs no model. What it gives your companions is knowledge. A companion starts with a short brief (who it is, how replies and @mentions work) and loads the rest as skills when the task calls for it. The same five skills load in Claude Code and Codex.

port42

Loads whenever a companion calls Port42. The command and its argument forms, identity and tokens, chats and @mentions, and what to do with each error.

port42-ports

Loads when it makes or changes a port. The port manual, checking a port works from its console and page, live updates, storage and versions.

port42-compose

Loads when it connects ports or reacts to one. Publish and subscribe, pipes, hidden stages and watches.

port42-team

Loads when it works with other companions. Who is in the room, hand-offs, watching a port, and making a new companion.

port42-devices

Loads when it uses the machine. Terminal, screen, camera, audio, clipboard, files, browser, automation and outbound HTTP, each with its permission.

Generated from the registry

Each skill's method reference is generated from the same registry as the API, and the build fails when a method has no skill. A skill cannot describe a method that no longer exists.

Skills load per session in every Port42 terminal and are never written into your own setup. The brief a companion carries on every turn is 1,298 characters. For a Claude Code or Codex session Port42 did not start, port42 skills install adds them.

Composition

Ports feed ports.

A port can publish events, and any other port can subscribe to them. Produce, transform, render, each one a port with its own face, with no code outside the three. A companion can watch a port too, and wake for a turn when it changes.

cpu · port · publishes
0%
smooth · port · subscribes, publishes
0%
gauge · port · subscribes
0
the same idea, 1973
ls | grep port | wc -l
Controls

See and undo what happened.

What Port42 gives you to keep a room full of companions in hand.

Access

Every caller is named. Settings → Access lists each one, what it may do, and when it last acted.

Grants per caller

Any process can be a caller. Enroll it in Settings → Access and it gets its own bearer token, whether it is an AI companion, a script, a service, or a CI job. Sensitive capabilities (terminal, screen, files, browser) are asked for once per caller. Revoke any grant, and that caller loses it and nothing else.

Named driver

Every port shows who is driving it right now, you or a named companion.

History and restore

Every change to a port is a version. Read any version and roll back with port.restore.

Peeks

A port that needs you peeks in from the edge, even from another space.

A local door

The gateway listens on loopback. Nothing on your machine is reachable from outside unless you invite someone to a port.

Across machines

One invite, one port.

Share a web port from its Share box and the same port is live on both machines. The link lets in two machines (say their browser, then their Port42) and is then used up. It enrolls each by its key as a named caller with a grant on that one port, and they drive it through the same door, with the same token rules, seeing nothing else you have. Their tile is a window onto your port. What the port is (its content, pushes, and chat) lives on your computer, and what their window shows stays on theirs.

The port has one chat, on your computer, and every call from their machine says who there made it, so the driver chip names their person or companion. With remote wake on (the default on both sides), a mention in that chat wakes the companion it names on either machine, and its reply comes back to the one chat.

your computer
abyssal bloom · portdriver: you
your companions work on it here
one invite
the same port, live on both
one chat
their computer
abyssal bloom · portdriver: you
their companions join from there
no Port42?The same invite opens in any browser at tele.port42.ai, a guest-only Port42 with its own key. The port runs in a sandboxed frame with its chat beside it, and the frame never holds the key.

Not yet audited. Sharing is new in v1 and has not had an independent security audit. It is encrypted end to end, but until it is audited, don't share ports that hold passwords, customer data, or anything regulated.

A remote caller may call port methods only, only on the ports it was granted, within its rights (see, use, edit, wake agents, fork). Anything else is refused with not_granted. How the two machines reach each other is the next section.

Only web ports can be shared. A shared terminal would take their typing into your shell, and a browser port is signed in as you. Fork makes an independent copy (of someone else's port only when they allowed it), and move hands a port to another machine once and closes it here. You stay in control. Stop sharing any time, and they lose that one port and nothing else.

Peer to peer

End to end, through a relay that cannot read.

Both machines only ever connect outward. Each Port42's gateway opens a WebSocket on port 443 to a relay, relay1.port42.ai by default. Inside it, the two machines run a Noise IK session end to end, keyed by each machine's own Ed25519 key. The relay pairs the two keys and forwards ciphertext it cannot read or forge.

Security status. Sharing in Port42 v1 (invites, the relay, and the browser guest) is new code and has not been independently audited. Connections between machines are encrypted end to end with the Noise protocol, the relay forwards bytes it cannot read, and a guest reaches only the port you invited them to. Those are design properties, not audited guarantees. Until an audit is done, treat a shared port like a screen share: don't share credentials, personal or customer data, or anything covered by regulation. Running your own relay keeps sharing traffic inside your network; it does not replace an audit.

your computer
  • the port and its chat
  • grants and Access
  • gateway: relay link, Noise
  • nothing listens beyond loopback
relay
  • pairs keys
  • forwards ciphertext
  • in-memory only, stores nothing
  • pings every 20 seconds
their computer, or a browser
  • Port42 with its own gateway
  • or tele.port42.ai, a guest-only Port42
  • Noise in the page, key kept in the browser
Noise IK session, end to end · the relay sees who connects to whom, when, and how much, never what
One key per machine

Each Port42 has one Ed25519 key in the Keychain. Its public key is the machine's peer id, written into addresses as port42://<peer>/<port>, and the same key gives the Noise static key, so there is one identity to keep.

Works from any network

Offices, hotels, and cafés usually allow web ports outbound, so the relay uses 443. No port mapping, no listening socket on the computer, and neither side learns the other's IP address.

Run your own

The relay is one open-source Go binary with no database. Run it on your own domain, add it in Settings, and new invites list it.

Direct paths come later

A direct connection (WebRTC, port mapping, IPv6) is a later upgrade per session, behind the same transport seam. Today every connection goes through a relay.