Repository navigation
Releases: latere-ai/cella
Releases · latere-ai/cella
Release list
v0.22.3
Fixed
- On Kubernetes, a sandbox can write to its home directory. The root
file system is read only, so a tool that writes underHOME, such as
npm install -gor Claude Code saving its settings, failed with
ENOENTorEROFS.HOMEis now the workspace unlessspec.envsets
it, as on the native runtime, and what is written there survives a
restart. A sandbox started before this keeps the image's home until its
next start. Every sandbox now has a Secret, which holds at least that
path, since the Pod reads each of its variables from the sandbox's
Secret. - An update that changes the boundary of a sandbox on the local runtime
reaches it. The local runtime enforces the boundary with its own proxy,
and it kept the boundary the sandbox was created with, so an update that
narrowed the allow list was reported as enforced while commands still
reached the hosts it removed. Every command started after the update now
gets the new boundary; a process already running keeps its own until the
next start. An update the runtime does not take fails and changes
nothing.
v0.22.2
Fixed
- A streamed answer reaches an HTTP/1.1 workload with its end marked.
When the gateway substituted into a request whose destination streamed
its answer over HTTP/2, as a model's is, a workload that spoke HTTP/1.1
read the whole stream and then waited forever for the connection to
close. Claude Code hung on its first request this way. The stream is
now sent chunked, and the connection serves the next request.
v0.22.1
Fixed
- A request the gateway substitutes into answers in the protocol the
workload spoke. When the gateway reached the destination over HTTP/2,
a workload that spoke HTTP/1.1 read a status line namingHTTP/2.0.
curl read it; Bun, and so Claude Code, refused it as a connection error,
so Claude Code could not reach a model through a mounted secret.
v0.22.0
Added
make runcan leave its ports to the kernel.CELLA_RUN_PORT=0 make runstartscelladon two ports the kernel picks, so a script or
a test never chooses a port that another process takes beforecellad
binds it. The stack prints the URL as before and writes the addresses
celladbound toout/run/cellad.addrs, one role and its address per
line, the way the stubs' go toout/run/stubs.addrs.
Changed
- The egress gateway connects only to public addresses. It judges the
address each connection dials, after the name resolved, on both doors
and for a request it substitutes a secret into, so a name that resolves
to a private, loopback, link-local, carrier-grade NAT or unique local
address is refused like the address written out. The workload is
answered403, and the connection record saysNonPublicAddress; a
request refused inside a tunnel the gateway terminates is recorded on
its own, beside the tunnel. An installation whose sandboxes reach a
host on its own network, such as a registry, a mirror or an OAuth token
endpoint, names those ranges inCELLA_EGRESS_PRIVATE_RANGESon the
gateway; without it, those connections are now refused. A range that
reaches loopback, link-local or the unspecified address stops the
gateway at start, since no boundary admits those. - The reverse door reads a port. A destination written as
host:portin the first path segment is decided by its host, refused
before any dial when that host is an address no boundary admits, and
dialed and recorded at the port it names. Before, the port was part of
the host the boundary read, and the record said443whatever was
dialed. - The control plane's host is decided by the boundary like any other.
The gateway no longer refuses every connection to the host in
CELLA_URL. An installation that serves the control plane on a host it
shares with other services, under one API host, left its sandboxes
unable to reach any of them; a sandbox now reaches that host when its
boundary admits it. TheControlPlanereason no longer appears in
connection records. Thecellaclient still reads no proxy variable,
so a sandbox's own token never passes through the gateway. - The driver suite holds the ports it names.
runtimetestreserves
each port a case hands a sandbox's server, on a loopback socket of the
host that never listens, from the moment it picks the port until the
case ends, so no other process the machine runs takes it first. Where a
sandbox shares the host's network, as a native one does,
Options.EchoandOptions.Listenbind the port withSO_REUSEPORT:
a command that binds the port on loopback without it is refused, and on
Linux one that binds it on every address is too. A sandbox with a
network of its own binds as before.
Fixed
- A secret mounted on a workload that exists reaches its environment.
An owner who added a secret to a workload by an update had its value in
the gateway at once, but the placeholder never reached the workload's
environment, not even after a restart. The driver now records the new
environment, and the workload has the placeholder from its next start;
a running process keeps the environment it started with. The same holds
when a secret'sinjectmoves to another header or query parameter,
which changes its<env>_HEADERor<env>_QUERYcompanion. A driver
that did not take the new environment, such as a worker that was not
connected, fails no apply: the next apply or secret change hands it
again. - A secret mounted by its
sec_id keeps its placeholder. Each
rotation, and each apply, gave such a mount a new placeholder in the
gateway's map, while the workload kept the one it started with, so the
secret stopped being substituted after its first rotation. - An update that fails after its map reached the gateway puts the old
map back. An update refused by the driver, or one whose write failed,
left the gateway holding the map it asked for while the workload stayed
as it was. The gateway now holds the map the workload has, with the
placeholders it holds. make runno longer reads the last run's addresses. A restart over
the kept state directory could read the stubs' addresses of the run
before, a moment before the new stubs wrote theirs, and startcellad
against an issuer that was gone.
v0.21.1
Fixed
- A sandbox created right after its worker registers waits for the
worker's stream. An environment readsReadyfrom the moment a
worker's registration is answered, a moment before the worker opens its
stream, and a sandbox created on it in between failed with
CreateFailed. It now staysPendinguntil the stream opens and then
runs. The control plane logsa worker's stream opened, with the
worker's id, each time a stream opens, since a registration alone
already counts instatus.workers.
v0.21.0
Added
- A device's network can be
any: any public host but those you
block. Besideallowlistandnone, the host policy oflatere devicestakesnetwork.mode: anywith anetwork.blockedlist of
hosts: a host, an address or*.example.comfor the names below
example.com(notexample.comitself, so block both), alone or as
host:port. With it, everything readable in a granted folder can be
sent to any public host, and the connect screen, its facts and a notice
at every start say so. Whatever a name resolves to, a command still
reaches nothing on your computer, its Wi-Fi and Ethernet networks, or
any address that is not public.network.hostsunderanynames a
port beyond 443 and 80 ashost:port.anyis never the default, and
ProposePolicynever proposes it: a connect query asking for it is
refused, and a file that had it proposes the allow list of the default
hosts again. It is enforced by macOS's own sandbox alone; elsewhere, and
in a program that does not callhostsandbox.Main, a connect, a change
or a start that names it is refused with the new code
device_network_unenforceable, never run as another network.cellad
takes the same asCELLA_LOCAL_NETWORK=anywithCELLA_LOCAL_BLOCKED. - A refused connection is told. Each host a session's command is
refused, by this computer's network or the session's own, reaches every
subscriber of the engine as anEventNetworkRefusedand the activity
log as anetwork_refusedline, with the host and port and the session,
agent and sandbox it came from, never the request. A command repeating
a refusal is told once a minute.latere devicesprints the line, and
GET /v1/approvals?follow=1carries the event. - A refused host can be allowed for one session, or saved.
Engine.AllowSessionHost(ctx, session, host, port)lets that session's later
commands reach the host, without changing the host policy file, until
the session's sandbox stops or is deleted, or the engine stops, pauses
or reloads.Engine.SaveHost(host, port)writes it into the policy, and
every session's next command reaches it at once, with no restart, so no
running session stops. A command that runs keeps the network it started
with: the host reaches the next one. Underany, an allowance lifts the
policy's own block of the host by name; a host a*.entry blocks is
refused with the new codedevice_host_not_allowed, as is a network of
none. The control socket answers both asPOST /v1/sessions/{session}/hostsandPOST /v1/policy/hostswith{"host", "port"}; an unknown session is404 no_session. Each allowance is a
host_allowedorhost_savedline in the activity log. - For embedders:
local.OptionstakesOnRefused,Allowedand
Isolation;local.NetworktakesBlocked;local.HostEntrybuilds
the entry a host and port are allowed as, andlocal.Network.Allow
saves one. An embedder that shows its own words keys the new connect
factnetwork_ports, and the codes above. - The manifest's host network has an
openmode. A worker whose
computer admits any public host registers{"mode": "open", "blocked": [...]}instatus.hosts. A sandbox askingopenthere getsopen
denying what it or the host blocks, one askingallowlistkeeps the
hosts the host does not block, and a local driver that declaresopen
holds that deny list itself, with no gateway. The control plane must
run this release before a computer servesany: an older one narrows
every sandbox on such a computer to no network.
Changed
latere.ai/x/pkgv0.109.0. Under macOS's own sandbox every command now
starts from its launcher, one with no network included, so a program
embedding the engine or the local runtime callshostsandbox.Mainas
the first statement ofmain(latereand Latere Studio do), and a
test binary that uses Seatbelt calls it inTestMain.- A command started in a sandbox gets the computer's network as it is
now: a sandbox started again after its computer narrowed its network
gets the narrower one, where before it kept the network it was created
with.
v0.20.4
Fixed
device.ForgetwithdropFiledeletes a host policy file that names no
device. Whenlatere devices removeremoved the computer while
latere devices connectwas serving it, the serving engine saw the
removal first and cleared the device from the file, and the file then
stayed with its folders, though removing says it deletes the file.
v0.20.3
Fixed
- On macOS, a command cannot get around a denial through a folder.
Throughlatere.ai/x/pkgv0.108.0, a command in a sandbox cannot clone
a folder, which would copy files it may not read into a copy it may;
cannot rename, swap or remove the folders between a granted folder and
a path denied inside it; and cannot create one of those folders that is
missing, so it cannot plant a denied path under a name it controls. A
granted folder with a long path, about 255 bytes or more, now runs
commands; before, each one exited 65. - A device creates
~/.configand~/.dockerbefore it serves a
folder above your home directory. Each holds a path the sandbox
always denies,~/.config/gcloudand~/.docker/config.json, so under
the rule above a session granted that folder could not create either,
and a tool that keeps its settings in one would fail. The engine that
latere devicesand an application embeddinglatere.ai/x/cella/device
run now creates each one that is missing, mode 0700, before it serves a
writable grant of a folder above your home directory. A name already
there, a link or a file included, is left as it is. A create that fails
stops the engine with the new codedevice_home_folder_failed, whose
detail names the folder and the cause; an embedder that shows its own
words adds a sentence for it. Your home directory itself is still
refused as a grant.
v0.20.2
Fixed
- A worker replaced before its stream opened now stops. On an
environment withspec.workers.max, a registration with the same key
from the same host that replaced a worker which had registered but not
yet opened its stream left that worker uninformed: its stream was
refused as one that never registered, so it registered again and took
the place back from its replacement. Its stream is now closed with
worker_replacedas soon as it opens, as a live worker's is, andRun
returns. A stream that opened before its key was revoked or its
environment deleted, and said hello after, is closed withkey_revoked
orserve_refusedthe same way.
v0.20.1
Added
device.PathsFrom(lookup)resolves the pathsdevice.DefaultPaths
does, with the XDG andLATERE_*variables read throughlookup
instead of the process's environment, for an application the desktop
starts that reads them from the person's login shell.
Fixed
- Built with Go 1.27.3, which fixes GO-2026-6809, a flaw in the
standard library's certificate verification that this code can reach.