# How Celld Differentiates Between Asset-Only Deployments and Worker Deployments

> Learn how celld differentiates asset-only and worker deployments by checking the manifest for a script field. Discover Celld's asset resolver logic.

- Repository: [Deno/celld](https://github.com/denoland/celld)
- Tags: internals
- Published: 2026-08-09

---

**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`](https://github.com/denoland/celld/blob/main/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.

```rust
// 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`](https://github.com/denoland/celld/blob/main/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.

```rust
// 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`](https://github.com/denoland/celld/blob/main/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.

```rust
// 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`](https://github.com/denoland/celld/blob/main/crates/celld/deploy.rs) to classify the deployment type at startup.
- **Flag storage**: The `AssetResolverInner` struct in [`crates/celld/assets.rs`](https://github.com/denoland/celld/blob/main/crates/celld/assets.rs) maintains an `asset_only` boolean that caches this classification.
- **Runtime behavior**: Request handlers in [`crates/celld/main.rs`](https://github.com/denoland/celld/blob/main/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`](https://github.com/denoland/celld/blob/main/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`](https://github.com/denoland/celld/blob/main/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`](https://github.com/denoland/celld/blob/main/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.