Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ COPY --from=build /src/dist/container/bin/ /usr/local/bin/
COPY --from=build /src/dist/worker-app/ /app/dist/worker-app/
COPY --from=build /src/dist/cloudflare/assets/ /app/dist/cloudflare/assets/
COPY --from=build /src/dist/container/wrangler.jsonc /app/wrangler.jsonc
ENV CELLD_ESBUILD=/usr/local/bin/esbuild CELLD_IDLE_EVICT_S=60
ENV CELLD_ESBUILD=/usr/local/bin/esbuild CELLD_LTX_RETENTION_SECS=3600
WORKDIR /app
USER 65532:65532
VOLUME ["/app/.celld"]
Expand Down
3 changes: 3 additions & 0 deletions cloudflare/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ export class ArtifactLibrary extends DurableObject<unknown> {
}
}
this.sql.exec("create index if not exists serve_events_version on serve_events(version_id, served_at)");
// History lists read only these columns. Covering them keeps listings from
// reading each revision's source, which precedes them in the row.
this.sql.exec("create index if not exists versions_history on versions(artifact_id, revision, id, created_at, source_hash, runtime, reason, restored_from)");
});
}

Expand Down
3 changes: 3 additions & 0 deletions cloudflare/scripts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ export class ScriptLibrary extends DurableObject<unknown> {
this.sql.exec(`alter table ${table} add column project text not null default '{"files":{},"dependencies":{},"lock":{}}'`);
}
}
// History lists read only these columns, newest first. Covering them keeps
// listings from reading each revision's source, which precedes them in the row.
this.sql.exec("create index if not exists script_versions_history on script_versions(created_at, id, workspace, name, revision, source_hash, reason, restored_from)");
}
private row(input: {workspace: string; name: string}): Row {
const {workspace,name} = key(input);
Expand Down
11 changes: 9 additions & 2 deletions docs/celld-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,18 @@ For production or multiple machines, use celld's bucket-backed node mode:
4. Terminate TLS and authenticate users in the application or a chosen reverse
proxy. Keep the operator/peer listener on a trusted private network, with an
encrypted overlay when that network does not provide confidentiality.
5. Use `/.well-known/celld/health` for node readiness. `/health` is an application
5. Set `CELLD_LTX_RETENTION_SECS` (for example, `3600`) on every node once all
nodes run 0.6.1. Without it, celld never deletes superseded snapshot epochs,
so bucket usage grows with each activation, including every wake after idle
eviction. Epoch deletion requires list-after-write consistency; see the
[storage guarantees](https://github.com/denoland/celld/blob/v0.6.1/docs/guarantees.md)
before enabling it on a multi-region bucket. Nodes using `bucket` durability
delete nothing. Unset it on every node before rolling back to celld 0.6.0.
6. Use `/.well-known/celld/health` for node readiness. `/health` is an application
route and does not establish node readiness. Allow graceful SIGTERM shutdown:
the supervisor's stop grace must exceed celld's configured shutdown bound
(40 seconds by default), with enough time for the expected drain and handoff.
6. Update app code through `celld deploy`. Running nodes adopt deployments in
7. Update app code through `celld deploy`. Running nodes adopt deployments in
place; a failed build leaves the previous deployment serving. For runtime
upgrades, follow the release-specific shutdown requirements. The
[0.6.0 release](https://github.com/denoland/celld/releases/tag/v0.6.0) requires
Expand Down
10 changes: 10 additions & 0 deletions docs/daemon.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,16 @@ credentials or other environment variables. The native server binds only to
loopback. Its state is local application data and is not uploaded or backed up
automatically.

celld stores full copies of an object's database each time the object is unloaded
and started again. The host keeps idle objects in memory, so this happens when the
server restarts or celld unloads objects under memory pressure, not after each idle
period. Setting `CELLD_IDLE_EVICT_S` unloads objects after that many idle seconds,
and every later wake stores more copies. The host deletes copies that are older than
one hour and no longer needed for recovery (`CELLD_LTX_RETENTION_SECS`, default
`3600`). Deletion can take about ten minutes after the next write to that object.
Set `CELLD_LTX_RETENTION_SECS=0` to keep every copy; storage then grows with each
restart.

For bucket-backed production nodes, TLS/authentication boundaries, health checks,
and graceful rollout guidance, see [celld deployment](celld-deployment.md).

Expand Down
4 changes: 3 additions & 1 deletion docs/docker.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ docker run -d --name artifacts --restart unless-stopped \
Open [localhost:4786](http://127.0.0.1:4786); the MCP endpoint is
`http://127.0.0.1:4786/mcp`. The named volume preserves apps, databases, files,
and schedules across container replacements. Stop the container before backing
up the volume. Allow 60 seconds for graceful shutdown.
up the volume. Allow 60 seconds for graceful shutdown. The image deletes
superseded storage snapshots after one hour, as described in the
[host guide](daemon.md#data-and-configuration).

## Configure network access

Expand Down
6 changes: 5 additions & 1 deletion src/local/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,11 @@ export async function runCelldServer(options: { port?: number; stateDir?: string
const esbuild = esbuildRequire.resolve(`@esbuild/${process.platform}-${process.arch}/bin/esbuild`);
child = Bun.spawn([executable, "dev", project, "--host", "127.0.0.1", "--port", String(port), "--no-watch", "--logs"], {
cwd: project,
env: { ...process.env, CELLD_ESBUILD: esbuild, CELLD_IDLE_EVICT_S: process.env.CELLD_IDLE_EVICT_S ?? "60", RUST_LOG: process.env.RUST_LOG ?? "warn" },
// Each unload and restart of an object stores full copies of its database, so
// idle objects stay resident (celld still unloads them under memory pressure).
// Retention lets celld delete superseded copies.
env: { ...process.env, CELLD_ESBUILD: esbuild,
CELLD_LTX_RETENTION_SECS: process.env.CELLD_LTX_RETENTION_SECS ?? "3600", RUST_LOG: process.env.RUST_LOG ?? "warn" },
stdin: "ignore", stdout: "pipe", stderr: "pipe",
});
const forward = async (stream: ReadableStream<Uint8Array> | number | undefined) => {
Expand Down
Loading