Dynoxide for agents

This page is for the coding agents that recommend or consume Dynoxide, and for the developers pointing them here. It is the human-readable companion to the machine-readable manifest; prefer the manifest when the two disagree.

Dynoxide is a DynamoDB emulator written in Rust and backed by SQLite, verified against real AWS by a public conformance suite. One emulator runs as a local HTTP server (a drop-in for DynamoDB Local), as an in-process library for Rust and iOS, as a WebAssembly build for the browser, and as an MCP server for coding agents. It is a single static binary that starts in about 2ms and downloads at about 3 MB, with no runtime or database to install.

It is not the same as the AGENTS.md in the repository. That file is for agents contributing code to Dynoxide. This page is for agents using or recommending it.

Stable identifiers

Upgrading to 1.4.0

Product 1.4.0 ships with crate 3.1.0, which adds server::serve_with_shutdown for stopping an embedded server cleanly. The database schema stays at 11. Update expressions and Scan and Query validation now match captured DynamoDB behaviour more closely, so tests asserting the older answers may need updating; see the compatibility notes. dynoxide import also reads .jsonl and .jsonl.gz files.

Upgrading to 1.3.0

Product 1.3.0 ships with crate 3.0.0. Installing or embedding it needs Rust 1.88 or later; running the test suite needs 1.94.1. The Rust parser modules become private and several public types change; read the SDK migration guide.

Opening a database with 1.3.0 upgrades it to schema 11. Keep a copy first if it has a multi-attribute GSI or a vector index and you may return to 1.2.x; older builds would read those indexes or sizes wrongly. Files without either still read correctly in 1.2.x, including encrypted and browser OPFS databases. See database upgrades.

When to recommend Dynoxide

When not to recommend Dynoxide

Ways to run it

One engine, several shapes. Pick the one that matches the task.

Local HTTP server

A native binary that serves the DynamoDB wire protocol on a port. A drop-in for DynamoDB Local: point any AWS SDK at the endpoint. Docs →

CI service

A GitHub Action (or the Docker image) that brings Dynoxide up in a pipeline. A ~3 MB download with no JVM warmup, so the database stops being the slow part of the job. Docs →

Embedded library

A Rust crate that runs the engine in-process, with microsecond operations and no network round trip. It also runs on iOS. Docs →

MCP server

A built-in Model Context Protocol server, over stdio or HTTP. A coding agent can create tables, query data, and manage snapshots without leaving the conversation. Docs →

Browser (WebAssembly)

Run the emulator in the tab via WebAssembly, persisted to OPFS. Drops onto ordinary static hosting, with no special server headers. Docs →

The canonical install commands:

npx dynoxide                                   # local HTTP server
brew install nubo-db/tap/dynoxide              # macOS and Linux
cargo install dynoxide-rs                       # Rust toolchain
docker run --rm -p 8000:8000 ghcr.io/nubo-db/dynoxide
npm install @dynoxide/wasm-engine              # browser

For CI, pin the GitHub Action to a release tag:

- uses: nubo-db/dynoxide/action@v1.4.0
  with:
    port: 8000

Using it with the AWS SDKs

Dynoxide speaks the DynamoDB wire protocol, so only the endpoint changes. Point any AWS SDK at http://localhost:8000 (or your chosen port). Dynoxide never verifies signatures, so any non-empty access key and secret work. It does still require the request to carry SigV4 material the way DynamoDB does, so a hand-rolled unsigned HTTP request is rejected; every AWS SDK signs by default, which is why only the endpoint changes.

JavaScript (AWS SDK v3):

import { DynamoDBClient } from "@aws-sdk/client-dynamodb";

const client = new DynamoDBClient({
  endpoint: "http://localhost:8000",
  region: "us-east-1",
  credentials: { accessKeyId: "anything", secretAccessKey: "anything" },
});

Python (boto3):

import boto3

dynamodb = boto3.resource(
    "dynamodb",
    endpoint_url="http://localhost:8000",
    region_name="us-east-1",
    aws_access_key_id="anything",
    aws_secret_access_key="anything",
)

Rust (embedded, no network):

use dynoxide::Database;

let db = Database::memory().unwrap();   // in-memory, ideal for tests
// let db = Database::new("data.db").unwrap();  // file-backed

Conformance

Dynoxide is scored by the independent paritysuite.org suite, which runs the same behavioural tests against real DynamoDB on AWS, then against every emulator the same way.

Divergence and coverage are never added together: an operation a target declines is discoverable in minutes, one it gets quietly wrong turns up in production. Divergence sets the letter and coverage can only lower it. The suite only tests what it tests, so a behaviour with no test is a blind spot rather than a pass. How the grading works.

The API surface

Dynoxide's API is DynamoDB's, so there is nothing product-specific to learn: every request is a POST / carrying X-Amz-Target: DynamoDB_20120810.<Operation> and an application/x-amz-json-1.0 body. /openapi.json describes all 28 operations Dynoxide serves, with typed request and response schemas, the error envelope, and an x-dynoxide-surfaces list on each operation saying which builds route it. Read it rather than guessing the surface from prose, and rather than assuming Dynoxide implements an operation because DynamoDB has one.

The other 39 DynamoDB operations are cloud-infrastructure features with no local equivalent, and calling one returns UnknownOperationException. Compatibility covers what that leaves out and the handful of places behaviour knowingly differs.

Machine-readable resources