Skip to main content

Binary path

Both client types — Client (one-shot) and StreamClient (persistent) — require the path to the compiled dpf binary as their first argument.
import "your-project/internal/dpf"

// One-shot client
client := dpf.NewClient("./dpf/target/release/dpf")

// Streaming client (persistent process, lower latency)
stream, err := dpf.NewStreamClient("./dpf/target/release/dpf")
if err != nil {
    log.Fatal(err)
}
defer stream.Close()
You can use an absolute path, a path relative to the working directory, or a binary on $PATH:
// Absolute path (recommended for production)
client := dpf.NewClient("/usr/local/bin/dpf")

// From PATH
client := dpf.NewClient("dpf")
For production deployments, use the statically compiled musl binary (make build-rust-static). It has no shared library dependencies and runs on any Linux system regardless of the installed glibc version. Copy the binary from dpf/target/x86_64-unknown-linux-musl/release/dpf to your deployment target and reference its absolute path.

Timeout

Client defaults to a 30-second timeout per operation. Use SetTimeout to override this for long-running jobs such as video transcoding.
client := dpf.NewClient("./dpf/target/release/dpf")

// Default: 30 seconds (suitable for image operations)
// No change needed for most image workloads.

// Increase for video and audio processing
client.SetTimeout(5 * time.Minute)
The timeout is enforced via context.WithTimeout inside Execute. When the deadline is exceeded, the underlying exec.Cmd is cancelled and an error is returned to the caller.
// SetTimeout signature (from dpf.go)
func (c *Client) SetTimeout(d time.Duration)
Recommended timeout values by operation type:
Operation typeRecommended timeout
Image resize / optimize / convert30s (default)
Batch image processing2–5 min
Video transcode / resize5–15 min
Audio transcode / normalize2–5 min
StreamClient does not expose a per-operation timeout. The persistent process remains alive until you call Close(). If you need per-operation deadlines with streaming mode, wrap individual Execute calls with a context.WithTimeout before passing the job.

Logging

The dpf binary uses the env_logger crate. Set the RUST_LOG environment variable to control log verbosity. Logs are written to stderr and do not interfere with the JSON stdout protocol.
RUST_LOG=info ./dpf/target/release/dpf process \
  --job '{"operation":"resize","input":"image.png","output_dir":"/tmp","widths":[320,640]}'
Available log levels:
LevelDescription
errorOnly fatal errors
warnWarnings and errors
infoGeneral operation progress (recommended for production)
debugDetailed per-step trace
traceMaximum verbosity, includes internal state
You can also scope logging to specific modules:
# Log only the processor module at debug level
RUST_LOG=dpf::processor=debug ./dpf/target/release/dpf --stream

# Combine: info globally, debug for one module
RUST_LOG=info,dpf::operations=debug ./dpf/target/release/dpf caps
To configure the log level when invoking via the Go client, set the environment variable before starting the process:
os.Setenv("RUST_LOG", "info")

client := dpf.NewClient("./dpf/target/release/dpf")
Or pass it through the environment of the spawned command. For StreamClient, set it before calling NewStreamClient since the Rust process is started at construction time.

Static vs dynamic binary

DevPixelForge ships two build variants:
VariantBuild commandBinary pathUse case
Dynamicmake build-rustdpf/target/release/dpfLocal development
Static (musl)make build-rust-staticdpf/target/x86_64-unknown-linux-musl/release/dpfProduction, containers
The dynamic binary links against the system’s glibc. The static musl binary bundles all dependencies and is fully self-contained. For container images and systems where the glibc version may differ from the build host, always prefer the static binary.
// Development
client := dpf.NewClient("./dpf/target/release/dpf")

// Production (static binary deployed to /usr/local/bin)
client := dpf.NewClient("/usr/local/bin/dpf")

Full configuration example

package main

import (
    "context"
    "log"
    "os"
    "time"

    "your-project/internal/dpf"
)

func main() {
    // Point to the compiled dpf binary
    binaryPath := "/usr/local/bin/dpf"

    // Enable info-level logging from the Rust engine
    os.Setenv("RUST_LOG", "info")

    // One-shot client with extended timeout for video jobs
    client := dpf.NewClient(binaryPath)
    client.SetTimeout(10 * time.Minute)

    ctx := context.Background()
    result, err := client.Resize(ctx, "input.png", "/tmp/output", []uint32{320, 640, 1024})
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("resize completed in %dms, %d outputs", result.ElapsedMs, len(result.Outputs))

    // Streaming client for high-throughput workloads
    stream, err := dpf.NewStreamClient(binaryPath)
    if err != nil {
        log.Fatal(err)
    }
    defer stream.Close()

    streamResult, err := stream.Execute(&dpf.ResizeJob{
        Operation: "resize",
        Input:     "banner.png",
        OutputDir: "/tmp/output",
        Widths:    []uint32{800, 1200},
    })
    if err != nil {
        log.Fatal(err)
    }
    log.Printf("stream resize: %dms", streamResult.ElapsedMs)
}

Build docs developers (and LLMs) love