Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
* [Authentication](for-developers/taco-sdk/references/authentication/README.md)
* [Condition Context](for-developers/taco-sdk/references/authentication/conditioncontext-and-context-variables.md)
* [Conditions](for-developers/taco-sdk/references/conditions/README.md)
* [Building Conditions with an LLM](for-developers/taco-sdk/references/conditions/building-with-llms.md)
* [Cookbook](for-developers/taco-sdk/references/conditions/cookbook.md)
* [Discord Tipping Bot Deep Dive](for-developers/taco-sdk/references/conditions/discord-tipping-bot-deep-dive.md)
* [Context Variables Cheatsheet](for-developers/taco-sdk/references/conditions/context-variables.md)
* [Validating Conditions](for-developers/taco-sdk/references/conditions/validating-conditions.md)
* [Troubleshooting](for-developers/taco-sdk/references/conditions/troubleshooting.md)
* [TimeCondition](for-developers/taco-sdk/references/conditions/timecondition.md)
* [RpcCondition](for-developers/taco-sdk/references/conditions/rpccondition.md)
* [ContractCondition](for-developers/taco-sdk/references/conditions/contractcondition/README.md)
Expand Down
11 changes: 11 additions & 0 deletions for-developers/taco-sdk/references/conditions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

This section focuses on `Condition` types, composition and usage.

{% hint style="success" %}
**Authoring conditions with an LLM?** Start with [Building Conditions with an LLM](building-with-llms.md). The four key references are:

- The [Cookbook](cookbook.md) — JSON examples for every condition type.
- The [Discord tipping bot deep-dive](discord-tipping-bot-deep-dive.md) — a fully annotated complex condition.
- The [Schema reference](https://github.com/nucypher/taco-web/blob/signing-epic/packages/taco/schema-docs/condition-schemas.md) — auto-generated source of truth (markdown). A [JSON Schema version](https://raw.githubusercontent.com/nucypher/taco-web/signing-epic/packages/taco/schema-docs/condition-schema.json) is available for editors and structured-output LLMs.
- The [Validator script](validating-conditions.md) — catch shape errors locally before hitting the network.

When something breaks, see [Troubleshooting](troubleshooting.md) and the [Context Variables cheatsheet](context-variables.md).
{% endhint %}

## Base Conditions

Base conditions define specific criteria, and each includes a `returnValueTest` to compare the actual execution result with the expected value. These include:
Expand Down
128 changes: 128 additions & 0 deletions for-developers/taco-sdk/references/conditions/building-with-llms.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
# Building Conditions with an LLM

TACo conditions are JSON. That makes them an excellent target for LLM-assisted authoring: describe the access policy you want in plain English, hand the LLM the right context, and iterate until the validator is happy.

This page is the recommended workflow.

## The context you give the LLM

Paste these four things into your LLM of choice (Claude, ChatGPT, Cursor, etc.) at the start of a new conversation:

1. **The full condition schema** — the canonical, machine-readable definition of every condition type, every field, every allowed value. Two formats are auto-generated from the same TypeScript source:
- [`condition-schemas.md`](https://github.com/nucypher/taco-web/blob/signing-epic/packages/taco/schema-docs/condition-schemas.md) — human-readable reference for prose-style LLMs.
- [`condition-schema.json`](https://raw.githubusercontent.com/nucypher/taco-web/signing-epic/packages/taco/schema-docs/condition-schema.json) — standard [JSON Schema](https://json-schema.org/) document. Use this with structured-output LLM APIs and editor `$schema` references (see [JSON Schema integration](#json-schema-integration) below).

Link these directly — do not vendor copies that will drift.
2. **The cookbook** — [JSON examples covering every condition type](cookbook.md). Examples teach an LLM patterns far faster than prose.
3. **The deep-dive** — the [Discord tipping bot walkthrough](discord-tipping-bot-deep-dive.md). One realistic, complex, fully-annotated condition is worth a thousand toy examples.
4. **The validator script** — [`validate-conditions.ts`](validating-conditions.md). Tell the LLM it can run this and iterate on the output.

## A prompt template

```
You are helping me author a TACo condition in JSON.

Reference material (please read before writing any condition):
- Schema (JSON Schema, machine-readable source of truth):
https://raw.githubusercontent.com/nucypher/taco-web/signing-epic/packages/taco/schema-docs/condition-schema.json
- Schema (prose version, same source):
https://github.com/nucypher/taco-web/blob/signing-epic/packages/taco/schema-docs/condition-schemas.md
- Cookbook of examples: https://docs.taco.build/for-developers/taco-sdk/references/conditions/cookbook
- Annotated complex example: https://docs.taco.build/for-developers/taco-sdk/references/conditions/discord-tipping-bot-deep-dive

Rules:
- Output a single JSON object (no prose around it) when I ask for a condition.
- Every field must exist in the schema. Do not invent fields.
- Context variables start with ":" and match /^:[a-zA-Z_][a-zA-Z0-9_]*$/.
- CompoundCondition: max 5 operands.
- MultiConditions (CompoundCondition, IfThenElseCondition, SequentialCondition)
share a combined nesting depth limit of 4.
- SequentialCondition: 2–20 variables.
- After each condition you produce, I will run validate-conditions.ts and
paste the output back. Fix any validation errors and try again.

What I want the condition to enforce:
<describe your access policy in plain English>
```

## The iteration loop

1. LLM produces a condition.
2. Save it as `conditions.json`.
3. Run `npx tsx validate-conditions.ts` ([source](validating-conditions.md)).
4. If invalid, paste the error back to the LLM. If valid, test it end-to-end against a local testnet (see [Quickstart](../../../access-control/quickstart-testnet.md)) or your app.

This loop usually converges in 1–3 rounds even for complex conditions.

## Tips that materially improve LLM output

- **Be explicit about the chain ID.** "Base mainnet" is ambiguous to a model that has not read your config; say `"chain": 8453`.
- **Name the data source.** "Check an NFT balance" is vague. "Call `balanceOf(:userAddress)` on contract `0xabc...` on Polygon (137) and require result `> 0`" is unambiguous.
- **Specify the comparator.** Models default to `==` even when you mean `>=`.
- **For sequential conditions**, list variables in dependency order and remind the model that later variables can reference earlier ones with `:varName`.
- **For ABI validation**, give the model the function signature (e.g. `transfer(address,uint256)`) — it cannot guess parameter order reliably.
- **When using `signing-attribute` / `signing-abi-attribute`**, mention which signing object format you are using (UserOperation, plain transaction, custom struct) so the model picks the right `attributeName`.

## JSON Schema integration

The auto-generated [`condition-schema.json`](https://raw.githubusercontent.com/nucypher/taco-web/signing-epic/packages/taco/schema-docs/condition-schema.json) is the highest-leverage piece of tooling available for condition authoring. It works in three places without any TACo dependency:

### Editors

Add `$schema` to the top of any `conditions.json` and your editor (VS Code, Cursor, JetBrains) will validate it inline as you type, with autocomplete for every field:

```json
{
"$schema": "https://raw.githubusercontent.com/nucypher/taco-web/signing-epic/packages/taco/schema-docs/condition-schema.json",
"version": "1.0.0",
"condition": {
"conditionType": "time",
"chain": 137,
"method": "blocktime",
"returnValueTest": { "comparator": ">", "value": 1735689600 }
}
}
```

### LLM structured output

Both Anthropic and OpenAI's APIs accept a JSON Schema directly to constrain model output. Example with the Anthropic SDK:

```ts
import Anthropic from '@anthropic-ai/sdk';

const conditionSchema = await fetch(
'https://raw.githubusercontent.com/nucypher/taco-web/signing-epic/packages/taco/schema-docs/condition-schema.json'
).then(r => r.json());

const result = await new Anthropic().messages.create({
model: 'claude-opus-4-6',
max_tokens: 2048,
tools: [{
name: 'emit_condition',
description: 'Emit a TACo condition matching the requested policy.',
input_schema: conditionSchema,
}],
tool_choice: { type: 'tool', name: 'emit_condition' },
messages: [{
role: 'user',
content: 'Build a condition that allows decryption only if the requester holds at least one NFT from collection 0xabc on Ethereum mainnet.',
}],
});
```

The model is now structurally constrained — it cannot return invalid shapes.

### Standalone validation (any language)

You no longer need `@nucypher/taco` installed to validate. Any standard JSON Schema validator works:

```bash
pnpm dlx ajv-cli validate \
-s https://raw.githubusercontent.com/nucypher/taco-web/signing-epic/packages/taco/schema-docs/condition-schema.json \
-d conditions.json --strict=false
```

The Python equivalent uses [`jsonschema`](https://python-jsonschema.readthedocs.io/), the Go equivalent uses [`gojsonschema`](https://github.com/xeipuuv/gojsonschema), etc.

The `validate-conditions.ts` script ([page](validating-conditions.md)) is still useful when you want runtime semantics (e.g. catching nesting-depth errors that JSON Schema cannot express), but for shape validation alone the JSON Schema is enough.
116 changes: 116 additions & 0 deletions for-developers/taco-sdk/references/conditions/context-variables.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Context Variables Cheatsheet

A **context variable** is a placeholder used inside a condition. Its value is supplied at decryption (or signing) time, not at encryption time. That is what makes a single encrypted ciphertext usable across many requesters and many runtime states.

This page is a cheatsheet. For a longer narrative, see [Condition Context](../authentication/conditioncontext-and-context-variables.md).

## Naming rules

- Always start with `:` — e.g. `:userAddress`
- After the `:`, the name must match `/^[a-zA-Z_][a-zA-Z0-9_]*$/`
- Case-sensitive
- No dots, dashes, or spaces. `:user-address`, `:user.address`, `:1stParam` are all invalid.

If you forget the leading `:`, the SDK will treat your value as a literal string and your condition will silently match nothing (or worse, match something).

## Built-in context variables

The SDK recognises a small set of reserved or default context-variable names. Their behaviour differs — read the table carefully before assuming a value is "automatic".

| Variable | Kind | Available in | Description |
| :--- | :--- | :--- | :--- |
| `:signingConditionObject` | **Auto-populated** (the only one) | `SigningObjectAttributeCondition`, `SigningObjectAbiAttributeCondition` | The full object (e.g. UserOperation) being submitted for threshold signing. You do not need to add this to the condition context — the signing flow injects it. |
| `:userAddress` | **Reserved** | All conditions | The Ethereum address of the requester. Its value can only be provided by an `AuthProvider` at request time — it is never set by user code directly. |
| `:message` | **Default name** | `EcdsaCondition` | Default for `EcdsaCondition.message`. Not automatically populated — the decrypter must supply a value at request time. |
| `:signature` | **Default name** | `EcdsaCondition` | Default for `EcdsaCondition.signature`. Not automatically populated — the decrypter must supply a value at request time. |
| `:jwtToken` | **Default name** | `JwtCondition` | Default for `JwtCondition.jwtToken`. Not automatically populated — the decrypter must supply a value at request time. |

### Auto-populated vs. reserved vs. default

- **Auto-populated** means the SDK / signing flow injects the value for you. Only `:signingConditionObject` is in this category.
- **Reserved** means the name is fixed and the value can only come from a specific source (for `:userAddress`, that source is an `AuthProvider`).
- **Default name** means the condition *defaults* to that variable name, but you can override it. For example, if a single compound condition contains two `JwtCondition`s that must validate two different tokens, give each its own variable:

```json
{
"conditionType": "compound",
"operator": "and",
"operands": [
{
"conditionType": "jwt",
"jwtToken": ":idpToken",
"publicKey": "…",
"expectedIssuer": "https://idp.example.com/"
},
{
"conditionType": "jwt",
"jwtToken": ":partnerToken",
"publicKey": "…",
"expectedIssuer": "https://partner.example.com/"
}
]
}
```

The same applies to `EcdsaCondition.message` and `EcdsaCondition.signature` when verifying multiple signatures in one condition.

## Custom context variables

You can define any name you like, as long as it follows the [naming rules](#naming-rules). The decrypter is responsible for supplying its value at decryption time.

```json
{
"conditionType": "json",
"data": ":discordPayload",
"query": "$.member.user.id",
"returnValueTest": { "comparator": ">", "value": 0 }
}
```

Here `:discordPayload` is a custom variable. The bot supplies it as part of the decryption request.

### Variables produced by SequentialCondition

`SequentialCondition` lets each step bind a `varName`. That name becomes a context variable usable by subsequent steps:

```json
{
"conditionType": "sequential",
"conditionVariables": [
{
"varName": "balance",
"condition": { "...": "fetches an ERC20 balance" }
},
{
"varName": "validate",
"condition": {
"conditionType": "context-variable",
"contextVariable": ":balance",
"returnValueTest": { "comparator": ">=", "value": 1000 }
}
}
]
}
```

`varName` is a **plain string** (no leading `:`), but you reference it later **with** the `:` prefix.

## Where context variables can appear

| Field type | Accepts context variable? |
| :--- | :--- |
| `returnValueTest.value` | ✅ |
| `ContractCondition.parameters[*]` | ✅ |
| `JsonCondition.data` | ✅ (required — must be a context variable) |
| `JsonCondition.query` | ✅ (or a JSONPath) |
| `JsonApiCondition.endpoint` | ❌ (must be a literal HTTPS URL) |
| `JsonApiCondition.authorizationToken` | ✅ |
| `EcdsaCondition.message` / `signature` | ✅ |
| `JwtCondition.jwtToken` | ✅ |
| `chain`, `method`, `comparator` | ❌ — must be literal |
| ABI function signature keys (`"transfer(address,uint256)"`) | ❌ — must be literal |
| ABI parameter `value` inside `returnValueTest` | ✅ |

## Common gotcha: JSONPath vs context variable

`JsonCondition.query` accepts **either** a JSONPath expression (`$.member.user.id`) **or** a context variable (`:somePath`). The schema disambiguates by the leading character — `$` means JSONPath, `:` means context variable. Anything else is a validation error.
Loading