Changelog

Release notes for Dynoxide. Pulled from the source CHANGELOG.

0.9.13

Security

  • Close a DNS rebinding vulnerability in the MCP HTTP transport (GHSA-89vp-x53w-74fx / CVE-2026-42559) by upgrading rmcp from 1.1.1 to 1.6.0 in both lockfiles. A malicious page could make the user's browser send requests to a loopback MCP server with a non-loopback Host header, which the server would then process. Affects 0.9.3 to 0.9.12. Users running dynoxide mcp --http or dynoxide serve --mcp should upgrade; stdio transport is unaffected.

  • Close a related cross-origin CSRF gap: a page could fetch the loopback endpoint with mode: 'no-cors', and the Host check would pass while the Origin header went unchecked. Affected write tools: put_item, update_item, delete_item, create_table, and batch_write_item. Fixed by setting an explicit Host and Origin allowlist on StreamableHttpServerConfig. Native MCP clients (Claude Code, Cursor, the dynoxide CLI) don't send an Origin header and are unaffected.

0.9.12

Fixed

  • Unix: port releases immediately after dynoxide serve shuts down. The listener used to skip SO_REUSEADDR, leaving leftover TIME_WAIT sockets from connected clients to block restart for ~60s. Live-listener conflict detection is unaffected: SO_REUSEADDR only bypasses TIME_WAIT, not active sockets.

    Windows: unchanged. SO_REUSEADDR lets another process hijack an active bind there, so we leave it off.

0.9.11

Fixed

  • dynoxide serve --mcp now exits cleanly on Ctrl+C when an MCP client (Claude Code, Cursor) is holding a connection open. The MCP server's graceful-shutdown drain used to wait for those connections forever, hanging the process until something SIGKILLed it (#22)

Security

  • Refresh Cargo.lock for the dependabot patches reachable within MSRV: aws-lc-sys 0.37.1 to 0.40.0 (5 high-severity AWS-LC issues), openssl 0.10.75 to 0.10.79 (5 buffer-overflow advisories), rand 0.8.5 to 0.8.6. Remaining rustls-webpki / time / aws-sdk-dynamodb alerts are dev-dependency only (test-suite AWS SDK chain, not the production binary) and stay pinned by MSRV 1.85 until v0.10.0

0.9.10

Fixed

  • 16 places where dynoxide's error strings drifted from real AWS DynamoDB. Mostly small things you only notice when you assert the message: tableName length validation is now per-operation (1 char on read/write, 3 stays on CreateTable), Select enum order matches AWS rather than alphabetical, Query vs Scan Limit=0 messages are different on purpose now, batch/transact empty and oversize requests use the standard validation envelope, and UpdateExpression/ProjectionExpression syntax errors include the AWS near: "..." window (#11, #12, #13, #15, #16, #17, #18)
  • TransactGetItems with a bad action key now comes back as a TransactionCanceledException with ValidationError rather than HTTP 500. The 500 was a real leak: the dedup loop called the server-fault helper before key validation (#19)

0.9.9

Fixed

  • KeyConditionExpression now accepts parenthesised sub-expressions, matching DynamoDB. Forms like (#pk = :pk) AND (#sk = :sk) previously returned ValidationException: Expected attribute name, got (. Both outer-wrap and per-condition parens are now handled (#4, #7)
  • UpdateItem and TransactWriteItems.Update now evaluate ConditionExpression against the existing item before populating key attributes for upsert. Previously attribute_exists(pk) on a non-existent key succeeded and created a ghost item (#5)
  • Paginated Scan on a GSI now returns all items when multiple items share the same GSI partition key. Previously the second page returned 0 items because the pagination cursor used only (gsi_pk, gsi_sk) instead of the full 4-tuple primary key (#6)
  • <> on missing attributes now returns true, matching DynamoDB. All other comparison operators continue to return false on missing operands. Previously <> also returned false, breaking PutItem conditional idioms like status <> "working" against fresh keys (#8)

0.9.8

Fixed

  • Dynoxide no longer orphans when backgrounded in npm scripts (dynoxide & sleep 1 && npm run seed && react-router dev) -- the port is released when the parent process exits (nubo-db/dynoxide#2)
  • The Rust server now handles SIGTERM for graceful shutdown, not just SIGINT (Ctrl+C) -- kill <pid> now works as expected
  • The npm wrapper switches from spawnSync to async spawn with explicit signal forwarding (SIGINT, SIGTERM, SIGHUP) and double-signal SIGKILL escalation
  • Parent-death detection via PPID polling catches the backgrounded case where no signal is delivered to the wrapper

0.9.7

Fixed

  • Benchmark sanity checks were blocking README updates during release - 10 stale values from the v0.9.6 pipeline now corrected
  • Binary download size in README was wrong (~5 MB, actually ~3 MB compressed / ~6 MB on disk)
  • Docker image sizes now show both download and on-disk measurements - the old "225 MB" was the compressed download, the actual on-disk size is 471 MB
  • MCP tool count in README was 33, should be 34 - execute_transaction_partiql was missing from the list
  • npm README had incorrect --input and --db-path flags for the import command (should be --source, --schema, --output)
  • Dropped the serve subcommand from npm examples (bare dynoxide --port 8000 is the preferred form)

Changed

  • Restructured release pipeline for token efficiency and reliability - dispatch verification, idempotent crate/npm publishing, template-based Homebrew formula updates
  • npm publishing uses OIDC provenance via a dedicated npm.yml workflow
  • Cross-compilation switched to cargo-zigbuild for aarch64-musl targets
  • Commit Cargo.lock for reproducible CI builds (was previously gitignored)
  • Updated npm package README to reflect current CLI usage and features

Security

  • Updated aws-lc-sys 0.37.1 to 0.39.1 (10 high-severity advisories - PKCS7 verification bypass, timing side-channel in AES-CCM, CRL/name constraint issues)
  • Updated rustls-webpki 0.103.9 to 0.103.10 (2 medium-severity CRL Distribution Point matching issues)

0.9.6

Fixed

  • Statically link the MSVC C runtime on Windows so the release binary no longer requires VCRUNTIME140.dll
  • Switch Linux aarch64 target to musl for fully static binaries (matching x86_64)

Changed

  • Drop the separate x86_64-unknown-linux-gnu release target (the musl build is already fully portable)

0.9.5

Added

  • DynamoDB conformance suite — 526 independently written tests across 3 tiers, validated against real DynamoDB ground truth. Dynoxide: 100%. DynamoDB Local: 92%. See dynamodb-conformance.
  • Dynalite external conformance — 817/1039 passing (87.1% DynamoDB parity) against Dynalite's test suite, where real DynamoDB itself only passes 51%
  • DynamoDB compatibility audit — code-verified compatibility matrix with file/line references, public-facing summary with DynamoDB Local comparison column, prioritised gap tracking
  • Correctness audit — 41 issues identified and resolved across core operations and PartiQL
  • Reserved word validation — 573 DynamoDB reserved keywords rejected in ConditionExpression, UpdateExpression, FilterExpression, and ProjectionExpression with correct error messages
  • README benchmark automation — CI benchmark numbers auto-updated via template markers and Python script; PR-based review with sanity checking
  • IdempotentParameterMismatchException — TransactWriteItems detects same token with different payload
  • AccessDeniedException — returned for tag operations on non-existent ARNs (matches DynamoDB behaviour)

Changed

  • BigDecimal replaces f64 for all number comparisons and arithmetic — eliminates silent precision loss beyond 15 significant digits; f64 fast-path for ≤15 significant digits preserves performance
  • PartiQL INSERT now fails with DuplicateItemException if item already exists (previously silently overwrote)
  • PartiQL tokeniser — correct handling of negative numbers, escaped single quotes, unknown characters (error instead of silent skip)
  • Query/Scan COUNT now returns filtered count, not scanned count, when FilterExpression is present
  • begins_with sort key — SQL LIKE wildcards (%, _) properly escaped
  • Condition + write operations wrapped in SQLite transactions to prevent TOCTOU races
  • 1MB response limit now counts all scanned items, not just filtered results
  • GSI query/scan LastEvaluatedKey now includes base table key attributes
  • BatchWriteItem rejects duplicate keys within the same request
  • TransactWriteItems — 4MB size check uses accurate item size calculation; CancellationReasons returned as structured top-level JSON field; ReturnValuesOnConditionCheckFailure returns ALL_OLD item on condition failure
  • UpdateItem rejects empty update expressions; protects key attributes from REMOVE/ADD/DELETE
  • ReturnValues validated against allowed values per operation
  • UnprocessedKeys in BatchGetItem preserves per-table settings
  • SET on list index beyond bounds extends the list with NULL padding (previously returned error)
  • SET on empty list at index 0 now succeeds
  • Projection with list index correctly reconstructs list structure (previously created Map where List was needed)
  • Select validation — invalid Select values and SPECIFIC_ATTRIBUTES without ProjectionExpression rejected
  • ConsistentRead on GSI rejected with correct error message
  • Limit of 0 rejected with constraint error
  • Query/Scan validation ordering matches DynamoDB (input validation before table existence check)
  • Expression attribute usage validated syntactically (at parse time) not semantically (at runtime) — fixes false positives with if_not_exists short-circuiting
  • SerializationException pre-checks for non-list field types with DynamoDB-compatible error format
  • Error type prefixValidationException uses com.amazon.coral.validate# prefix matching real DynamoDB
  • BatchExecuteStatement uses short error codes (ResourceNotFound not fully qualified type) and rejects empty Statements array
  • UpdateTable GSI delete returns ResourceNotFoundException for non-existent GSI (previously ValidationException)
  • StreamSpecification included in DescribeTable response
  • Stack overflow protection: 32-level nesting depth limit on item validation (matches DynamoDB)
  • AND/OR short-circuit evaluation in condition expressions

Fixed

  • size() function no longer evaluates on invalid attribute types
  • Idempotency tokens correctly compared in TransactWriteItems
  • PutItem no longer double-reads item for conditional checks
  • GSI sort key replacement handles all edge cases
  • Nested projection preserves document structure (no longer flattens)
  • Double-quote identifier escaping in PartiQL
  • PartiQL DELETE with missing sort key returns proper error
  • PartiQL nested SET paths create correct nested structure (no longer creates literal dot-notation keys)
  • PartiQL SELECT with nested map paths resolves correctly
  • TTL expiry cleans up LSI entries (previously left orphans)
  • GSI/LSI name collision detected and rejected at CreateTable time
  • LSI pagination uses composite cursor to handle duplicate sort key values
  • ExecuteTransaction breaks on first failure (previously continued executing then rolled back)
  • Partition size calculation for ItemCollectionMetrics sums across base table and all LSI tables
  • Error message fidelity improvements across empty string, deletion protection, scan segment, and query validation messages

0.9.4

Added

  • Local Secondary Indexes (LSI) — full lifecycle: creation, query/scan routing, projection types (ALL, KEYS_ONLY, INCLUDE), sparse index behaviour, write path maintenance across all operations including TTL expiry
  • ExecuteTransaction — PartiQL transactional execution with all-or-nothing semantics, condition checks, per-statement cancellation reasons, ConsumedCapacity support
  • Parallel Scan — SQLite-level segment filtering via registered FNV-1a scalar function; validated segment/total parameters
  • CreateTable extensionsSSESpecification, TableClass (validated), Tags (inline), DeletionProtectionEnabled with enforcement on DeleteTable and toggle via UpdateTable
  • PartiQL WHERE clause extensionsBETWEEN, IN, CONTAINS, IS MISSING, IS NOT MISSING, OR, NOT, parenthesised grouping
  • PartiQL nested path projectionsSELECT address.city, tags[0] FROM ... with correct nested structure preservation
  • PartiQL REMOVE clauseUPDATE ... REMOVE attribute
  • PartiQL SET expressions — arithmetic (count + 1), list_append, if_not_exists in SET clauses
  • PartiQL IF NOT EXISTSINSERT ... VALUE {...} IF NOT EXISTS
  • PartiQL set literals<< 'a', 'b', 'c' >> syntax for SS/NS/BS
  • PartiQL COUNT(*) and LIMIT support
  • Item validation — empty string/set rejection, number precision validation (38 significant digits, ±9.99E+125 range), set deduplication (NS by numeric equivalence)
  • Unused expression attribute rejection — unreferenced ExpressionAttributeNames/ExpressionAttributeValues entries return ValidationException
  • ReturnItemCollectionMetrics — partition collection size across base table and all LSI tables
  • Per-GSI ConsumedCapacityINDEXES mode returns per-GSI breakdown in GlobalSecondaryIndexes map

Changed

  • TrackedExpressionAttributes — unified expression resolution with usage tracking; removed duplicate untracked code paths (~400 LOC reduction)
  • ScanParams / QueryParams structs replace parameter sprawl in storage layer
  • CreateTableMetadata consolidates previously triple-duplicated row mapping
  • GSI/LSI secondary indexes on (base_pk, base_sk) / (table_pk, table_sk) columns — eliminates full table scans during index maintenance
  • Schema v5 migration with automatic secondary index creation on existing tables

0.9.3

Added

  • MCP Server — 33 tools exposing DynamoDB operations for coding agents (Claude Code, Cursor, etc.)
    • stdio and Streamable HTTP transports
    • --read-only, --max-items, --max-size-bytes safety flags
    • bulk_put_items tool for batch loading
    • OneTable --data-model integration with entity-aware agent context and --data-model-summary-limit
    • --mcp flag on dynoxide serve to run MCP alongside HTTP server
    • Snapshots: create_snapshot, restore_snapshot, list_snapshots, delete_snapshot with auto-snapshot before delete_table
    • get_database_info tool with data model context
  • Import CLIdynoxide import for DynamoDB Export data (JSON Lines format)
    • Anonymisation rules: fake, mask, hash, redact, null actions
    • Cross-table consistency for specified fields
    • zstd compression (--compress)
    • --continue-on-error, --tables filtering, atomic --force overwrite
    • Stream-aware import (reproduces source table's StreamSpecification)
  • CLI restructuringdynoxide serve, dynoxide mcp, dynoxide import subcommands
  • Database introspection and port conflict detection on startup
  • RUST_LOG debug tracing throughout HTTP and MCP servers

0.9.2

Added

  • SQLCipher encryptionencryption feature (vendored OpenSSL via SQLCipher) and encryption-cc feature (Apple CommonCrypto backend) for encryption at rest
  • Secure key handling via --encryption-key-file or DYNOXIDE_ENCRYPTION_KEY environment variable
  • UpdateTableStreamSpecification support, GSI create/delete with backfill
  • Tag operationsTagResource, UntagResource, ListTagsOfResource
  • ReturnValuesOnConditionCheckFailure for TransactWriteItems
  • GitHub Actionnubo-db/dynoxide@v1 with optional snapshot-url preloading
  • Homebrew formulabrew install nubo-db/tap/dynoxide
  • Release CI workflow with cross-platform binary builds (Linux x86_64/aarch64/musl, macOS Intel/Apple Silicon, Windows)
  • Private-to-public repo publishing pipeline
  • DynamoDBStreams target prefix — server accepts DynamoDB_20120810.ListStreams and Streams-prefixed actions
  • From/TryFrom conversions for request/response types
  • item! macro for ergonomic item construction in tests
  • Table metadata cache for reduced SQLite round-trips
  • Stripped release binaries

Changed

  • nubo-appnubo-db GitHub organisation rename

0.9.1

Added

  • Server and X-Dynoxide-Version headers on all HTTP responses
  • TableArn, LatestStreamArn, and related ARN fields in API responses
  • Comprehensive benchmarking suite comparing Dynoxide against DynamoDB Local and LocalStack
    • Criterion, iai-callgrind, and custom benchmark binaries
    • CI workflows for regression detection and historical tracking
    • Standard 13-step workload with JVM warmup protocol

Changed

  • http-server feature is now enabled by default
  • Package renamed to dynoxide-rs for crates.io publishing

Fixed

  • Rustdoc warnings
  • README version reference

0.9.0

Added

  • Core DynamoDB emulator backed by SQLite via rusqlite
  • In-memory and persistent database modes
  • Table operations: CreateTable, DeleteTable, DescribeTable, ListTables
  • Item operations: PutItem, GetItem, DeleteItem, UpdateItem
  • Query and Scan with full expression support and pagination
  • Batch operations: BatchGetItem, BatchWriteItem
  • Transactions: TransactWriteItems, TransactGetItems
  • Global Secondary Indexes (GSI)
  • DynamoDB Streams (all four view types)
  • TTL with background sweep
  • Full expression language: KeyCondition, Filter, Condition, Projection, Update
  • PartiQL: ExecuteStatement, BatchExecuteStatement
  • ReturnConsumedCapacity (TOTAL and INDEXES modes)
  • HTTP server (axum-based, DynamoDB JSON wire protocol)
  • 300+ tests