Agent skill

Stash Dynamodb

by cipherstash in cipherstash/stack

Integrate CipherStash encryption with Amazon DynamoDB using @cipherstash/stack/dynamodb and EQL v3 schemas.

MITAuto-check passedDatabases

Install Stash Dynamodb

skills CLI
$ npx skills add cipherstash/stack --skill stash-dynamodb -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install cipherstash/stack stash-dynamodb --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/cipherstash/stack.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/stash-dynamodb .claude/skills/stash-dynamodb && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
stash-dynamodb
GitHub stars
157
Token cost
~5.7k tokens
SKILL.md length
1,845 words
Files
1
Skills in repo
14
Repo updated
First seen
Licence
MIT

At a glance

Integrate CipherStash encryption with Amazon DynamoDB using @cipherstash/stack/dynamodb and EQL v3 schemas.

  • Works in 2 steps: Define Encrypted Schema (EQL v3) → Initialize Clients
  • Tasks that involve NoSQL databases
  • SKILL.md covers When to Use This Skill, Installation, How It Works and Schema and Stored Versions, plus 9 more sections
  • Calls npm, npx and node

What it does

Stash Dynamodb is an agent skill from cipherstash/stack. Integrate CipherStash encryption with Amazon DynamoDB using @cipherstash/stack/dynamodb and EQL v3 schemas. Covers item and bulk encryption, legacy v2 reads, HMAC query attributes, nested objects, audit logging, and the source/hmac storage convention.

Its SKILL.md is about 5.7k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It sits in Databases, covering NoSQL databases. It works with Amazon DynamoDB, PostgreSQL and Prisma. The repository describes itself as: Searchable, application-level encryption for building privacy-first apps. The licence is MIT.

When your agent uses it

  • Tasks that involve NoSQL databases

Example prompts

  • “/stash-dynamodb”

Requirements

  • Node.js

Workflow steps

2 steps, taken from the step headings in SKILL.md.

  1. Define Encrypted Schema (EQL v3)
  2. Initialize Clients

What it can do on your machine

Read from SKILL.md and the folder at commit 415b62c. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • npm
    • npx
    • node
    • pnpm
    • yarn

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use npm, npx, pnpm and yarn, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Stash Dynamodb loads about 5.7k tokens when it runs. Until then it costs about 68 tokens; SKILL.md has 1,845 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~68
When it runs · the whole SKILL.md, loaded when a task matches
~5.7k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from cipherstash/stack at commit 415b62c, republished under its MIT licence (© cipherstash). 1,845 words, ~5,717 tokens.

Download SKILL.mdSave it as .claude/skills/stash-dynamodb/SKILL.md (or your agent's skills folder).
name
stash-dynamodb
description
Integrate CipherStash encryption with Amazon DynamoDB using @cipherstash/stack/dynamodb and EQL v3 schemas. Covers item and bulk encryption, legacy v2 reads, HMAC query attributes, nested objects, audit logging, and the __source/__hmac storage convention.

CipherStash Stack - DynamoDB Integration

Guide for integrating CipherStash field-level encryption with Amazon DynamoDB using @cipherstash/stack/dynamodb. The helper encrypts items before writing to DynamoDB and decrypts them after reading - it does not wrap the AWS SDK, so you keep full control of your DynamoDB operations.

When to Use This Skill

  • Adding field-level encryption to DynamoDB items
  • Encrypting sensitive attributes before PutItem/BatchWrite
  • Decrypting attributes after GetItem/BatchGet/Query/Scan
  • Querying DynamoDB using encrypted partition or sort keys
  • Building applications where PII or sensitive data is stored in DynamoDB
  • Implementing audit logging for DynamoDB encryption operations

Installation

bash
npm install @cipherstash/stack @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb

Version note: npx stash init is the preferred install path — it pins every @cipherstash/* package to the versions matching your CLI release. If you install manually as above, verify what actually resolved (node -p "require('@cipherstash/stack/package.json').version"): bare dist-tag installs can lag behind a release, and stash init will warn on the version skew.

How It Works

CipherStash encrypts each attribute into two DynamoDB attributes:

Original AttributeStored AsPurpose
emailemail__sourceEncrypted ciphertext
emailemail__hmacHMAC — written whenever the domain produces an equality term, usable only for equality lookups (see the domain table below)

Non-encrypted attributes pass through unchanged. On decryption, the __source and __hmac attributes are recombined back into the original attribute name with the plaintext value.

Only equality is usable on DynamoDB. Ordering terms and free-text bloom filters have no DynamoDB query surface, so they are not stored. A column in an ordering or free-text domain still encrypts and decrypts correctly — it just cannot back a key condition.

Schema and Stored Versions

Schema authoring and every write are EQL v3-only. Existing v2 DynamoDB items remain readable by passing the same v3 table descriptor plus { storedEqlVersion: 2 } to decryptModel or bulkDecryptModels. Both entries serve legacy reads — the default @cipherstash/stack one and @cipherstash/stack/wasm-inline.

There is no infrastructure migration between the versions — DynamoDB has no EQL extension to install and no schema to alter — and there is no automatic data migration either. To fully move a table to v3, re-encrypt every item with the v3 schema.

The client must be built for the table. Build the client with the same v3 table you hand to encryptedDynamoDB — Encryption({ schemas: [users] }) returns the typed v3 client for a concrete v3 schema set. Passing a v3 table to a client that never registered it (a client built for a different schema set) throws a clear error naming the table on the first operation, instead of failing later with an opaque FFI deserialization error.

DynamoDB items are natively nested. Declare encrypted leaves as flat dotted paths; the item keeps its nested shape and unlisted siblings stay plaintext:

typescript
const users = encryptedTable("users", {
  "profile.ssn": types.TextEq("profile.ssn"),
  "profile.note": types.Text("profile.note"),
})
jsonc
{ "pk": "u#1",
  "profile": {
    "ssn__source": "<ciphertext>",
    "ssn__hmac": "<hmac>",        // equality term — FilterExpression only, not a key condition
    "note__source": "<ciphertext>",
    "city": "Sydney"              // not in schema, stays plaintext
  } }

A nested equality term like profile.ssn__hmac lives inside the profile map, so it can only be matched with a FilterExpression — DynamoDB key conditions and secondary-index keys must be top-level scalar attributes. If you need the HMAC to back a key condition or GSI, declare the field as a top-level column (ssn: types.TextEq("ssn")) so it is stored as a top-level ssn__hmac.

The dotted string is the property key as well as the column name — the model is matched by dotted path, so { profile: { ssn } } resolves correctly.

To encrypt a whole subtree as one value instead of per-leaf, use types.Json, which stores it as a single ste_vec attribute (profile__source).

Arrays are not descended into. The adapter splits encrypted leaves inside nested objects only. A value inside an array is stored whole — it is not split into __source/__hmac, so its ciphertext still decrypts on read but can never back a key condition, and it does not appear in the __source/__hmac attribute layout above. A DynamoDB key condition cannot target an array element in any case. To encrypt list data, either promote the searched field to a top-level column, or wrap the subtree in a single types.Json column.

Rolling Encryption Out to Production

DynamoDB encryption is single-deploy. There is no rollout/cutover split — unlike the Postgres path, DynamoDB has no row-level rename swap and no shared-state proxy. The application owns every write, so adding encryption is an application-side change that ships in one PR:

  1. Declare the encrypted schema (see Setup below).
  2. Build an encryptedDynamoDB helper and call encryptModel / decryptModel at your write and read sites.
  3. Ship the change.

For tables with existing populated items, the __source and __hmac attributes are added by the next write that touches each item. If you need every existing item encrypted at once (e.g. because a query uses email__hmac and would miss legacy items), run a one-shot script that reads every item, calls encryptModel, and writes it back. Idempotent: re-running an already-encrypted item is a no-op as long as the schema hasn't changed.

Where am I? Run stash status (or bunx/pnpm dlx/yarn dlx per your runner) for a project-wide view across both Postgres and DynamoDB integrations. DynamoDB columns surface in the quest log as already-complete since there is no staged lifecycle to track.

Setup

1. Define Encrypted Schema (EQL v3)

Each types.* factory is a concrete domain with fixed query capabilities. There are no chainable index methods — the type is the capability.

typescript
import { encryptedTable, types } from "@cipherstash/stack/v3"

const users = encryptedTable("users", {
  email: types.TextEq("email"),      // equality -> queryable via email__hmac
  name: types.Text("name"),          // storage only
  phone: types.Text("phone"),        // storage only
  age: types.IntegerOrd("age"),      // decryptable, NOT queryable on DynamoDB
  metadata: types.Json("metadata"),  // JSON document
})

Which domains give you a __hmac attribute you can query on:

Domain family__hmac written?Notes
types.TextEq, IntegerEq, BigintEq, DateEq, TimestampEq, NumericEq, RealEq, DoubleEq, SmallintEqYesThe equality domains — use these for anything you query
types.TextOrd, TextOrdOre, TextSearchYesText equality is HMAC-based even on ordering/search domains
types.IntegerOrd, DateOrd, TimestampOrd, and the other non-text *Ord/*OrdOreNoEquality resolves through an ordering term in Postgres, which DynamoDB cannot use
types.Text, Integer, Boolean, and the other bare domainsNoStorage only
types.JsonNoIndex terms live inside the ste_vec array; not splittable into an attribute

Rule of thumb: if an attribute will appear in a KeyConditionExpression, declare it with an *Eq domain.

2. Initialize Clients
typescript
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"
import { Encryption } from "@cipherstash/stack"
import { encryptedDynamoDB } from "@cipherstash/stack/dynamodb"

const dynamoClient = new DynamoDBClient({ region: "us-east-1" })
const docClient = DynamoDBDocumentClient.from(dynamoClient)

const encryptionClient = await Encryption({ schemas: [users] })
const dynamo = encryptedDynamoDB({ encryptionClient })

Audit metadata on decrypt works. decryptModel / bulkDecryptModels are audit-chainable — dynamo.decryptModel(item, table).audit({ metadata }) forwards the metadata to ZeroKMS on the default @cipherstash/stack entry. The @cipherstash/stack/wasm-inline client has no chainable operations, so audit metadata is dropped there.

Reading Existing EQL v2 Items

Use the current v3 table descriptor and select the stored wire version on the read. No v2 builder or v2-configured client is needed.

Works on both entries. Schema authoring is EQL v3-only everywhere, but the read itself is not: the legacy path reconstructs the v2 envelope around the current v3 table, and decrypt accepts either wire generation. So Deno, Bun, Workers and Supabase Edge Functions can read legacy items through @cipherstash/stack/wasm-inline too.

typescript
const decrypted = await dynamo.decryptModel(
  storedV2Item,
  users,
  { storedEqlVersion: 2 },
)
Optional: Logger and Error Handler
typescript
const dynamo = encryptedDynamoDB({
  encryptionClient,
  options: {
    logger: {
      error: (message, error) => console.error(`[DynamoDB] ${message}`, error),
    },
    errorHandler: (error) => {
      // Send to monitoring, etc.
      console.error(`[${error.code}] ${error.message}`)
    },
  },
})

Encrypt and Write

Single Item
typescript
import { PutCommand } from "@aws-sdk/lib-dynamodb"

const user = {
  pk: "user#1",
  email: "alice@example.com",  // will be encrypted
  name: "Alice Smith",         // will be encrypted
  role: "admin",               // not in schema, passes through
}

const result = await dynamo.encryptModel(user, users)

if (result.failure) {
  console.error("Encryption failed:", result.failure.message)
} else {
  await docClient.send(new PutCommand({
    TableName: "Users",
    Item: result.data,
    // result.data looks like:
    // {
    //   pk: "user#1",
    //   email__source: "<ciphertext>",
    //   email__hmac: "<hmac>",
    //   name__source: "<ciphertext>",
    //   role: "admin",
    // }
  }))
}
Bulk Items
typescript
import { BatchWriteCommand } from "@aws-sdk/lib-dynamodb"

const items = [
  { pk: "user#1", email: "alice@example.com", name: "Alice" },
  { pk: "user#2", email: "bob@example.com", name: "Bob" },
]

const result = await dynamo.bulkEncryptModels(items, users)

if (!result.failure) {
  await docClient.send(new BatchWriteCommand({
    RequestItems: {
      Users: result.data.map(item => ({
        PutRequest: { Item: item },
      })),
    },
  }))
}

Read and Decrypt

Single Item
typescript
import { GetCommand } from "@aws-sdk/lib-dynamodb"

const getResult = await docClient.send(new GetCommand({
  TableName: "Users",
  Key: { pk: "user#1" },
}))

const result = await dynamo.decryptModel(getResult.Item, users)

if (!result.failure) {
  console.log(result.data)
  // { pk: "user#1", email: "alice@example.com", name: "Alice Smith", role: "admin" }
}
Bulk Items
typescript
import { BatchGetCommand } from "@aws-sdk/lib-dynamodb"

const batchResult = await docClient.send(new BatchGetCommand({
  RequestItems: {
    Users: {
      Keys: [{ pk: "user#1" }, { pk: "user#2" }],
    },
  },
}))

const result = await dynamo.bulkDecryptModels(
  batchResult.Responses?.Users ?? [],
  users,
)

if (!result.failure) {
  for (const user of result.data) {
    console.log(user.email) // plaintext
  }
}

Querying with Encrypted Keys

DynamoDB queries use key conditions, so you need to encrypt the search value into its HMAC form. Use encryptionClient.encryptQuery() to get the HMAC, then use it in your key condition.

Show full SKILL.md (730 more words)Show less
Encrypted Partition Key

When an encrypted attribute is the partition key (e.g., email__hmac):

typescript
import { QueryCommand } from "@aws-sdk/lib-dynamodb"

// 1. Encrypt the search value to get the HMAC.
//    On an EQL v3 equality domain this mints the bare term — `{ v, i, hm }`
//    with no ciphertext — so `hm` is used directly.
const queryResult = await encryptionClient.encryptQuery("alice@example.com", {
  table: users,
  column: users.email,
})

if (queryResult.failure) {
  throw new Error(`Query encryption failed: ${queryResult.failure.message}`)
}

const emailHmac = queryResult.data.hm

// 2. Use the HMAC in a DynamoDB query
const result = await docClient.send(new QueryCommand({
  TableName: "Users",
  KeyConditionExpression: "email__hmac = :email",
  ExpressionAttributeValues: {
    ":email": emailHmac,
  },
}))

// 3. Decrypt the results
const decrypted = await dynamo.bulkDecryptModels(result.Items ?? [], users)
Encrypted Sort Key

When an encrypted attribute is the sort key:

typescript
const result = await docClient.send(new GetCommand({
  TableName: "Users",
  Key: {
    pk: "org#1",              // partition key (plain)
    email__hmac: emailHmac,   // sort key (encrypted HMAC)
  },
}))

const decrypted = await dynamo.decryptModel(result.Item, users)
Encrypted Attribute in GSI

When querying a Global Secondary Index where the GSI key is an encrypted HMAC:

typescript
const result = await docClient.send(new QueryCommand({
  TableName: "Users",
  IndexName: "EmailIndex",
  KeyConditionExpression: "email__hmac = :email",
  ExpressionAttributeValues: {
    ":email": emailHmac,
  },
  Limit: 1,
}))

if (result.Items?.length) {
  const decrypted = await dynamo.decryptModel(result.Items[0], users)
}

Audit Logging

All operations support .audit() chaining for audit metadata:

typescript
const result = await dynamo
  .encryptModel(user, users)
  .audit({
    metadata: {
      sub: "user-id-123",
      action: "user_registration",
      timestamp: new Date().toISOString(),
    },
  })

DynamoDB Table Design Considerations

Attribute Naming

For each encrypted field with an equality index, two attributes are stored:

  • {field}__source - The encrypted ciphertext (binary/string)
  • {field}__hmac - Deterministic HMAC for equality lookups

Fields without equality capability only get __source (no HMAC, so they can't be queried) — that means EQL v2 columns without .equality(), and EQL v3 columns outside the domains listed in the table above.

Key Schema Design
PatternPartition KeySort KeyUse Case
Plain PKpk (plain)-Standard lookup by ID
Encrypted PKemail__hmac-Lookup by encrypted attribute
Encrypted SKpk (plain)email__hmacComposite key with encrypted sort
GSI on HMACpk (plain)-Query by encrypted attribute via GSI with email__hmac as GSI PK
What You CAN Query
  • Equality on __hmac attributes (exact match only)
  • attribute_exists(email__source) / attribute_not_exists(email__source) in condition expressions
What You CANNOT Query
  • Range/comparison on encrypted attributes (no BETWEEN, <, > on __source)
  • Substring matching on encrypted attributes (no begins_with, contains on __source)
  • __source values are encrypted binary - only equality via __hmac is supported

Error Handling

All operations return Result<T, EncryptedDynamoDBError> with either data or failure:

typescript
const result = await dynamo.encryptModel(user, users)

if (result.failure) {
  console.error(result.failure.message)
  console.error(result.failure.code)
  // code: ProtectErrorCode | "DYNAMODB_ENCRYPTION_ERROR"
  console.error(result.failure.details)
}

Complete API Reference

encryptedDynamoDB(config)
typescript
import { encryptedDynamoDB } from "@cipherstash/stack/dynamodb"

const dynamo = encryptedDynamoDB({
  // From Encryption(...)
  encryptionClient,
  options: {         // optional
    logger: { error: (message, error) => void },
    errorHandler: (error) => void,
  }
})
Instance Methods

All methods accept an EQL v3 table. Decrypt defaults to stored EQL v3 and can reconstruct a legacy v2 envelope when explicitly requested.

EQL v3 — the item is checked against the table's column domains, and the result is typed as the attribute map that is actually stored: a declared column email becomes email__source (plus email__hmac if its domain mints one), NOT email.

MethodSignatureResolves to
encryptModel(item, v3Table)EncryptedAttributes<Table, T>
bulkEncryptModels(items, v3Table)EncryptedAttributes<Table, T>[]
decryptModel(storedItem, v3Table, readOptions?)DecryptedAttributes<Table, T> — __source folded back to the column, __hmac dropped
bulkDecryptModels(storedItems, v3Table, readOptions?)DecryptedAttributes<Table, T>[]

Let T be inferred from the argument; do not pass explicit type arguments on the v3 path.

For a stored v2 item, pass { storedEqlVersion: 2 } as readOptions. The table is still the current v3 descriptor, which supplies table and column identity.

That descriptor must be one of the tables you passed to Encryption({ schemas }). The adapter forwards it to the client to drive envelope and Date reconstruction, and the client rejects a table it was not initialized with — so a legacy read of a table your current schema no longer declares fails with decryptModel received a table this client was not initialized with. Keep the table declared for as long as you still need to read its v2 rows.

MethodSignatureResolves to
decryptModel(item, v3Table, { storedEqlVersion: 2 })T
bulkDecryptModels(items, v3Table, { storedEqlVersion: 2 })T[]

Grouped v2 fields. A v2 column inside a group was stored as <group>.<leaf>__source while the v2 schema knew it only as <leaf>. On a { storedEqlVersion: 2 } read the leaf is matched inside the group, so carrying the column forward as a plain top-level amount: types.TextEq('amount') reads those rows correctly. You can also name it by its full path, keeping the original DB name — 'details.amount': types.TextEq('amount'). Note the two differ: the property is the dotted path, the argument is the v2 DB name. This applies to v2 storage only; a v3 nested field uses the same dotted path for both ('profile.ssn': types.TextEq('profile.ssn')).

Type reconstruction follows either spelling: a grouped v2 date / timestamp column comes back as a Date, not an ISO string, whether you declare it as a plain top-level placedAt: types.Date('placed_at') or as the full dotted path.

All operations are thenable (awaitable) and support .audit({ metadata }) chaining. On the default @cipherstash/stack entry the metadata forwards to ZeroKMS on every operation, encrypt and decrypt alike (see the Setup note). The @cipherstash/stack/wasm-inline client has no .audit() — its operations return a plain promise — so audit metadata is dropped there (logged at debug level). The operation itself still succeeds; only the audit record is lost. Use the native entry when audit trails matter.

Types exported from @cipherstash/stack/dynamodb: EncryptedDynamoDBInstance, EncryptedDynamoDBConfig, EncryptedDynamoDBError, AnyEncryptedTable, DynamoDBReadOptions, DynamoDBEncryptionClient, EncryptedAttributes, DecryptedAttributes, AuditConfig.

Querying Encrypted Attributes

Use the encryption client directly (not the DynamoDB helper):

typescript
// EQL v3 — the domain fixes the query type, so no `queryType` is needed:
const result = await encryptionClient.encryptQuery(
  "search-value",
  { table: users, column: users.email }
)
// encryptQuery returns a Result; check the failure branch before reading `data`.
// `data?.hm` would mask a failure (and a null-plaintext result) as `undefined`,
// producing a malformed key condition rather than a clear error.
if (result.failure) throw new Error(result.failure.message)
const hmac = result.data.hm  // Use this in DynamoDB key conditions

// EQL v2 — pass queryType explicitly (`usersV2` is the legacy table declared
// in the v2 read section above; `users` is the v3 one and infers its queryType):
const v2Result = await encryptionClient.encryptQuery(
  "search-value",
  { table: usersV2, column: usersV2.email, queryType: "equality" }
)
if (v2Result.failure) throw new Error(v2Result.failure.message)
const v2Hmac = v2Result.data.hm

On a types.TextSearch column encryptQuery returns hm alongside ordering and bloom-filter terms regardless of queryType. Only hm is meaningful for DynamoDB — a free-text query cannot be expressed as a key condition.

Complete Example

typescript
import { DynamoDBClient } from "@aws-sdk/client-dynamodb"
import { DynamoDBDocumentClient, PutCommand, GetCommand, QueryCommand } from "@aws-sdk/lib-dynamodb"
import { Encryption } from "@cipherstash/stack"
import { encryptedTable, types } from "@cipherstash/stack/v3"
import { encryptedDynamoDB } from "@cipherstash/stack/dynamodb"

// Schema
const users = encryptedTable("users", {
  email: types.TextEq("email"),
  name: types.Text("name"),
})

// Clients
const dynamoClient = new DynamoDBClient({ region: "us-east-1" })
const docClient = DynamoDBDocumentClient.from(dynamoClient)
const encryptionClient = await Encryption({ schemas: [users] })
const dynamo = encryptedDynamoDB({ encryptionClient })

// Write
const user = { pk: "user#1", email: "alice@example.com", name: "Alice" }
const encResult = await dynamo.encryptModel(user, users)
if (!encResult.failure) {
  await docClient.send(new PutCommand({ TableName: "Users", Item: encResult.data }))
}

// Read by primary key
const getResult = await docClient.send(new GetCommand({
  TableName: "Users",
  Key: { pk: "user#1" },
}))
const decResult = await dynamo.decryptModel(getResult.Item, users)
if (!decResult.failure) {
  console.log(decResult.data.email) // "alice@example.com"
}

// Query by encrypted email (via HMAC)
const queryEnc = await encryptionClient.encryptQuery("alice@example.com", {
  table: users,
  column: users.email,
})
if (queryEnc.failure) throw new Error(queryEnc.failure.message)
const hmac = queryEnc.data.hm

const queryResult = await docClient.send(new QueryCommand({
  TableName: "Users",
  IndexName: "EmailIndex",
  KeyConditionExpression: "email__hmac = :e",
  ExpressionAttributeValues: { ":e": hmac },
}))

const decrypted = await dynamo.bulkDecryptModels(queryResult.Items ?? [], users)

© cipherstash, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in skills/stash-dynamodb of cipherstash/stack.

Open the folder on GitHubat commit 415b62c

Compare with similar skills

Stash Dynamodb next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Stash Dynamodb compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Stash Dynamodb this skillcipherstash/stack157—~5.7kAutomated safety check: PassMIT
DB SculptorEliasOulkadi/shokunin114—~3.1kAutomated safety check: NotesMIT
Prisma Database Setupcurvenote/curvenote1693 repos~1.4kAutomated safety check: PassMIT
Database FundamentalsDanielPodolsky/ownyourcode2901 repos~1.6kAutomated safety check: PassMIT
Database Expertcin12211/orca-q223—~2.8kAutomated safety check: PassMIT
Database Designerborghei/Claude-Skills874—~1.6kAutomated safety check: PassMIT

Similar skills

  • DB Sculptor

    EliasOulkadi/shokunin

    Design database schemas with Prisma/Drizzle, PostgreSQL index strategy (B-tree, GIN, GiST, BRIN, Hash), query optimization (EXPLAIN ANALYZE), migration safety (expand/contract, zero-downtime), and…

    114 GitHub stars~3.1k tokensUpdated 2 days ago
    DatabasesAuto-check: notes
  • Prisma Database Setup

    curvenote/curvenote

    Guides for configuring Prisma with different database providers (PostgreSQL, MySQL, SQLite, MongoDB, etc.).

    169 GitHub starsUsed in 3 repos~1.4k tokens
    DatabasesAuto-check passed
  • Database Fundamentals

    DanielPodolsky/ownyourcode

    Reviews schema design, SQL queries, ORM patterns. An agent skill from DanielPodolsky/ownyourcode.

    290 GitHub starsUsed in 1 repo~1.6k tokens
    DatabasesAuto-check passed
  • Database Expert

    cin12211/orca-q

    Database performance optimization, schema design, query analysis, and connection management across PostgreSQL, MySQL, MongoDB, and SQLite with ORM integration.

    223 GitHub stars~2.8k tokensUpdated 16 days ago
    DatabasesAuto-check passed
  • Database Designer

    borghei/Claude-Skills

    Database design with schema analysis, index optimization, and migration generation for PostgreSQL, MySQL, MongoDB, and DynamoDB.

    874 GitHub stars~1.6k tokensUpdated today
    DatabasesAuto-check passed
  • Better Drizzle

    almeidazs/better-drizzle

    Write, review, and debug code that uses better-drizzle, the typed repository layer over Drizzle ORM 1.x (better(db), client.users.findMany, paginate, cursor, upsertMany, relation include/connect…

    347 GitHub stars~1.7k tokensUpdated 2 days ago
    DatabasesAuto-check passed

More from cipherstash/stack

All 14 skills in this repo
  • Meta Issue Creation

    cipherstash/stack

    How an agent files a GitHub issue on cipherstash repos — required structure (Background / Problem / Proposal), dumbed-down wording rules, and pre-filing checks.

    157 GitHub stars~1k tokensUpdated today
    Auto-check passed
  • Meta PR Creation

    cipherstash/stack

    How an agent authors branches, commits, and pull requests on cipherstash/stack — naming, signed commits, the changeset/skills/meta-file checklist, and PR body structure with dumbed-down wording.

    157 GitHub stars~937 tokensUpdated today
    Auto-check passed
  • Stash Zerokms

    cipherstash/stack

    The ZeroKMS key model — keysets, clients, client keys, and the grant/revoke lifecycle.

    157 GitHub stars~4.4k tokensUpdated today
    Auto-check passed
  • Stash Deployment

    cipherstash/stack

    Deploy a CipherStash encryption rollout to a live environment without losing data — the multi-deploy ladder (schema-add + dual-write → backfill → read cutover → stop dual-writes → drop plaintext)…

    157 GitHub stars~6.5k tokensUpdated today
    Auto-check passed
  • Supply-chain security controls for the @cipherstash/stack monorepo.

    157 GitHub stars~5.2k tokensUpdated today
    Auto-check: warnings
  • Stash Drizzle

    cipherstash/stack

    Integrate CipherStash encryption with Drizzle ORM using @cipherstash/stack-drizzle (EQL v3).

    157 GitHub stars~10k tokensUpdated today
    Auto-check: notes

Categories

Questions about Stash Dynamodb

What does Stash Dynamodb do?

Integrate CipherStash encryption with Amazon DynamoDB using @cipherstash/stack/dynamodb and EQL v3 schemas. Stash Dynamodb is an agent skill from cipherstash/stack. Integrate CipherStash encryption with Amazon DynamoDB using @cipherstash/stack/dynamodb and EQL v3 schemas.

When should I use Stash Dynamodb?

Stash Dynamodb fits situations like: tasks that involve NoSQL databases.

How do I install Stash Dynamodb in Claude Code?

Run `npx skills add cipherstash/stack --skill stash-dynamodb -a claude-code`. Or copy the skill folder (skills/stash-dynamodb in cipherstash/stack) into .claude/skills/stash-dynamodb in your project. Claude Code loads it when a task matches its description.

How do I install Stash Dynamodb in Codex?

Run `npx skills add cipherstash/stack --skill stash-dynamodb -a codex`. Or copy the skill folder (skills/stash-dynamodb in cipherstash/stack) into .agents/skills/stash-dynamodb in your project. Codex loads it when a task matches its description.

Can I use Stash Dynamodb in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add cipherstash/stack --skill stash-dynamodb -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/stash-dynamodb, .gemini/skills/stash-dynamodb, .github/skills/stash-dynamodb and .opencode/skills/stash-dynamodb in your project.

What does Stash Dynamodb need to run?

Going by SKILL.md and its folder, Stash Dynamodb needs the command-line tools its instructions call (npm, npx, node, pnpm and yarn). Our summary lists: Node.js.

Does Stash Dynamodb access the network?

SKILL.md contains no URLs. Its commands use npm and npx, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Stash Dynamodb safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Stash Dynamodb use?

Stash Dynamodb is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Stash Dynamodb use?

About 5.7k tokens (SKILL.md is roughly 23k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Stash Dynamodb?

Skills that share tags, products or a category with Stash Dynamodb: DB Sculptor (EliasOulkadi/shokunin, 114 stars), Prisma Database Setup (curvenote/curvenote, 169 stars), Database Fundamentals (DanielPodolsky/ownyourcode, 290 stars) and Database Expert (cin12211/orca-q, 223 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Stash Dynamodb?

cipherstash (a GitHub organization) maintains it in cipherstash/stack, which has 157 GitHub stars. The repository holds 14 skills in this directory. The repository was last updated on October 7, 2026.

Source: cipherstash/stack on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.