How Celld Differentiates Between Asset-Only Deployments and Worker Deployments

Celld distinguishes asset-only deployments from Worker deployments by inspecting the deployment manifest for the presence of a script field; if the manifest contains no script and only defines assets, the deployment is flagged as asset-only via the AssetResolver.

The denoland/celld repository implements a Rust-based runtime for deploying JavaScript Workers and static assets. Understanding how Celld differentiates between asset-only deployments and Worker deployments is essential for configuring deployment manifests correctly. The runtime makes this determination at startup by parsing the deployment manifest and setting an internal flag that governs request routing behavior.

Deployment Manifest Inspection

Celld begins the differentiation process in crates/celld/deploy.rs by parsing the deployment Manifest. The manifest structure contains either a script field pointing to the Worker's main module or an assets section describing static files.

When the manifest includes a script, Celld treats the deployment as a Worker deployment capable of executing JavaScript. Conversely, if the manifest lacks a script field but contains an assets section, Celld classifies the deployment as asset-only. This binary determination happens during the initial loading phase before the runtime accepts any requests.

// crates/celld/deploy.rs – manifest parsing logic
let manifest = Manifest {
    script: Some("worker.js".into()),   // Worker deployment
    // or
    // assets: Some(asset_manifest),    // Asset-only deployment
};

The AssetResolver Flag

Once the manifest is parsed, Celld initializes the asset resolution system in crates/celld/assets.rs. The AssetResolverInner struct stores an asset_only boolean field that persists the deployment classification throughout the runtime lifecycle.

The flag is set to true precisely when manifest.script.is_none() evaluates to true during resolver construction. The AssetResolver exposes this state through the asset_only() accessor method, allowing other components to query the deployment type without re-examining the manifest.

// crates/celld/assets.rs
pub struct AssetResolverInner {
    asset_only: bool,               // Flag marking asset-only deployment
    // ...
}

impl AssetResolver {
    pub fn asset_only(&self) -> bool {
        self.inner.asset_only
    }
}

// Initialization sets the flag based on manifest presence
let resolver = AssetResolver::new(
    AssetResolverInner {
        asset_only: manifest.script.is_none(), // true for asset-only
        // ...
    }
);

Runtime Request Routing

During request handling in crates/celld/main.rs, Celld uses the AssetResolver to determine the execution path. When processing an incoming request, the runtime retrieves the resolver for the specific script or asset path and checks the asset_only() status.

If the resolver returns true, Celld bypasses the JavaScript runtime entirely and serves the static asset directly. If the resolver returns false, indicating a Worker deployment, Celld invokes the Worker handler to execute the script and generate a dynamic response.

// crates/celld/main.rs
match app.assets.get(&call.script) {
    Some(resolver) => {
        // Asset-only deployment → serve static asset directly
        if resolver.asset_only() {
            return asset_response(static_asset);
        }
        // Worker deployment → invoke the Worker handler
        // ...
    }
    None => { /* 404 handling */ }
}

Asset Bindings in Worker Deployments

Worker deployments can optionally include assets through an asset_binding field in the manifest. When this binding is present, the Worker can serve static files programmatically, but the deployment remains classified as a Worker deployment because the script field exists.

In this hybrid scenario, the asset_only flag remains false, ensuring the runtime still initializes the JavaScript environment and routes requests through the Worker script rather than serving assets directly. This distinction ensures that Worker logic—such as authentication headers or dynamic routing—can intercept asset requests when necessary.

Summary

  • Manifest inspection: Celld checks for the presence of a script field in crates/celld/deploy.rs to classify the deployment type at startup.
  • Flag storage: The AssetResolverInner struct in crates/celld/assets.rs maintains an asset_only boolean that caches this classification.
  • Runtime behavior: Request handlers in crates/celld/main.rs query resolver.asset_only() to decide between direct asset serving and Worker invocation.
  • Hybrid support: Worker deployments may include assets via asset_binding, but the presence of a script keeps asset_only set to false.

Frequently Asked Questions

What determines if a Celld deployment is asset-only?

A deployment is considered asset-only when the manifest loaded in crates/celld/deploy.rs contains no script field but does include an assets section. This condition sets the asset_only flag to true in the AssetResolver, causing the runtime to serve static files directly without initializing the JavaScript Worker environment.

Can a Worker deployment also serve static assets?

Yes. Worker deployments can include an optional asset_binding in the manifest that allows the Worker script to serve static assets programmatically. However, because the manifest contains a script field, the asset_only flag remains false, and all requests route through the Worker handler rather than being served directly by the asset resolver.

Where does Celld store the asset-only flag?

Celld stores the asset-only flag in the AssetResolverInner struct defined in crates/celld/assets.rs. The boolean field asset_only is initialized during resolver creation based on whether the deployment manifest includes a Worker script, and it is exposed through the AssetResolver::asset_only() method.

How does the runtime route requests differently for each deployment type?

In crates/celld/main.rs, the runtime retrieves the AssetResolver for the requested path and calls asset_only(). If this returns true, the runtime immediately returns the static asset. If false, the runtime proceeds to invoke the JavaScript Worker, allowing dynamic processing of the request.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →