AI policy
Two things govern what you may do with what MasterDB serves:
- The business’s AI policy — its own choices, sealed by a person at the business, as named booleans: how to treat its data as a source, which actions it permits, and the contexts it does not want its data used in. It can change at any time; the version in force when MasterDB served you is the one that governs, and every receipt names it.
- The AI-company Terms — the agreement your company signed, one versioned text for every AI company, including the use conditions: a record is used once, in one conversation, and is not cached or used for training.
// The AI policy: what the business permits, and the contexts it does not want its data used in, travels with// every row (as bits) and every fetch (as its sealed record). The key to the bits is public. The AI-company Terms// are one versioned text, apart.import assert from 'node:assert/strict';import { createPrivateKey } from 'node:crypto';import { readFileSync } from 'node:fs';import { SANDBOX, createEd25519Signer, createPublicClient, createRetrievalClient } from '@masterdb/client';
const signer = createEd25519Signer(createPrivateKey(readFileSync(process.env.MASTERDB_KEY_FILE as string)));const client = createRetrievalClient({ baseUrl: process.env.MASTERDB_API_URL ?? SANDBOX.retrieval, signer });const publicApi = createPublicClient({ baseUrl: process.env.MASTERDB_API_URL ?? SANDBOX.public });
// The key: every bit of every group, by name, and each blocked context's code (bc1…bc10). Cache it for a day.const key = await publicApi.GET('/v1/ai-policy-key');if (key.error) throw new Error(key.error.code);const K = key.data;
function decode(bits: { ai_policy_schema: number; use: number; action: number; blocked: number }) { if (bits.ai_policy_schema > K.current_ai_policy_schema) throw new Error(`unknown ai_policy_schema ${bits.ai_policy_schema}: refuse rather than guess`); const on = (mask: number, bit: number) => Math.floor(mask / 2 ** bit) % 2 === 1; const terms: Record<string, boolean> = {}; for (const e of K.groups.use) terms[e.name] = on(bits.use, e.bit); for (const e of K.groups.action) terms[e.name] = on(bits.action, e.bit); const blocked = K.blocked.filter((c) => c.since_schema <= bits.ai_policy_schema && on(bits.blocked, c.bit)); return { terms, blocked };}
// A row carries the bits in force for its business when it was served. Version 0 is a business with no AI policy// in force (never sealed, or refused at publish): every mask is 0 and nothing is permitted beyond the AI-company// Terms. Take a row whose business has a policy, to compare its bits with the sealed record a fetch carries.const search = await client.POST('/v1/search', { body: { collection: 'products', filter: { all: [{ country: 'US' }] }, sort_by: 'published_at:desc', limit: 20 },});if (search.error) throw new Error(search.error.code);const row = search.data.rows.find((r) => r.ai_policy_bits.ai_policy_version > 0);if (row === undefined) { console.log(`none of these ${search.data.rows.length} rows' businesses has an AI policy in force (version 0): nothing is permitted beyond the AI-company Terms`);} else { const fromRow = decode(row.ai_policy_bits); console.log('row', row.record_id, 'AI policy version', row.ai_policy_bits.ai_policy_version, fromRow.terms); for (const c of fromRow.blocked) console.log(`blocked ${c.code} ${c.name}: do not use this business's data to answer in this context`); if (!fromRow.terms['purchase']) console.log('this business does not permit an AI to complete a purchase');
// A fetch carries the business's sealed AI policy record itself: the named booleans, as the business sealed them. const fetched = await client.GET('/v1/records/{record_id}', { params: { path: { record_id: row.record_id } } }); if (fetched.error) throw new Error(fetched.error.code); const policy = fetched.data.ai_policy; if (policy.record === null) { // The fetch carries the policy in force when it is served; with no sealed record there, nothing to compare. console.log(`AI policy version ${policy.version} at fetch: no sealed record, nothing to compare`); } else { const sealed = policy.record as Record<string, unknown>; for (const [name, value] of Object.entries(fromRow.terms)) assert.equal(sealed[name], value, `${name}: the bits are derived from the sealed record`); const sealedBlocked = (sealed['blocked'] ?? {}) as Record<string, boolean>; assert.deepEqual(fromRow.blocked.map((c) => c.name), K.blocked.filter((c) => sealedBlocked[c.name] === true).map((c) => c.name)); assert.equal(policy.version, row.ai_policy_bits.ai_policy_version); }}
// The AI-company Terms in force, and any earlier version a receipt names.// `not_found` means no Terms version is in force in this environment.const terms = await client.GET('/v1/terms');if (terms.error?.code === 'not_found') { console.log('no AI-company Terms version is in force in this environment');} else { if (terms.error) throw new Error(terms.error.code); console.log('AI-company Terms version', terms.data.version, 'effective', terms.data.effective_from, terms.data.sha256); const v1 = await client.GET('/v1/terms/{version}', { params: { path: { version: 1 } } }); assert.equal(v1.data?.version, 1);}"""The AI policy: what the business permits, and the contexts it does not want its data used in, travels withevery row (as bits) and every fetch (as its sealed record). The key to the bits is public. The AI-company Termsare one versioned text, apart."""
import os
import httpxfrom masterdb_signing import SANDBOX_API, MasterDBAuth, load_key
client = httpx.Client( base_url=os.environ.get("MASTERDB_API_URL", SANDBOX_API), auth=MasterDBAuth(load_key(os.environ["MASTERDB_KEY_FILE"])),)
# The key: every bit of every group, by name, and each blocked context's code (bc1…bc10). Public, unsigned# (the sandbox serves the public paths on its API host). Cache it for a day.key = httpx.get(os.environ.get("MASTERDB_API_URL", SANDBOX_API) + "/v1/ai-policy-key").json()
def decode(bits: dict) -> tuple[dict[str, bool], list[dict]]: if bits["ai_policy_schema"] > key["current_ai_policy_schema"]: raise ValueError(f"unknown ai_policy_schema {bits['ai_policy_schema']}: refuse rather than guess") terms = {} for group in ("use", "action"): for e in key["groups"][group]: terms[e["name"]] = bool(bits[group] >> e["bit"] & 1) blocked = [c for c in key["blocked"] if c["since_schema"] <= bits["ai_policy_schema"] and bits["blocked"] >> c["bit"] & 1] return terms, blocked
# A row carries the bits in force for its business when it was served. Version 0 is a business with no AI policy# in force (never sealed, or refused at publish): every mask is 0 and nothing is permitted beyond the AI-company# Terms. Take a row whose business has a policy, to compare its bits with the sealed record a fetch carries.search = client.post("/v1/search", json={"collection": "products", "filter": {"all": [{"country": "US"}]}, "sort_by": "published_at:desc", "limit": 20})search.raise_for_status()rows = search.json()["rows"]row = next((r for r in rows if r["ai_policy_bits"]["ai_policy_version"] > 0), None)if row is None: print(f"none of these {len(rows)} rows' businesses has an AI policy in force (version 0): nothing is permitted beyond the AI-company Terms")else: from_row, blocked = decode(row["ai_policy_bits"]) print("row", row["record_id"], "AI policy version", row["ai_policy_bits"]["ai_policy_version"], from_row) for c in blocked: print(f"blocked {c['code']} {c['name']}: do not use this business's data to answer in this context") if not from_row["purchase"]: print("this business does not permit an AI to complete a purchase")
# A fetch carries the business's sealed AI policy record itself: the named booleans, as the business sealed them. fetched = client.get(f"/v1/records/{row['record_id']}") fetched.raise_for_status() policy = fetched.json()["ai_policy"] if policy["record"] is None: # The fetch carries the policy in force when it is served; with no sealed record there, nothing to compare. print(f"AI policy version {policy['version']} at fetch: no sealed record, nothing to compare") else: assert all(policy["record"][name] == value for name, value in from_row.items()), "the bits are derived from the sealed record" sealed_blocked = policy["record"].get("blocked", {}) assert [c["name"] for c in blocked] == [c["name"] for c in key["blocked"] if sealed_blocked.get(c["name"]) is True] assert policy["version"] == row["ai_policy_bits"]["ai_policy_version"]
# The AI-company Terms in force, and any earlier version a receipt names.# `not_found` means no Terms version is in force in this environment.terms = client.get("/v1/terms")if terms.status_code == 404 and terms.json().get("code") == "not_found": print("no AI-company Terms version is in force in this environment")else: terms.raise_for_status() current = terms.json() print("AI-company Terms version", current["version"], "effective", current["effective_from"], current["sha256"]) assert client.get("/v1/terms/1").json()["version"] == 1The AI policy, on a row
Section titled “The AI policy, on a row”Every row carries ai_policy_bits: {ai_policy_version, ai_policy_schema, use, action, blocked}, stamped as it is served from the business’s current sealed record, so you can honour what the business permits without fetching. use, action and blocked are bitmasks derived from the named booleans; ai_policy_schema says which name each bit is. Under ai_policy_schema 2, bit n (value 2^n) is:
| Bit | use |
action |
blocked |
|---|---|---|---|
| 0 | cite_as_source |
answer |
bc1 adult_sexual |
| 1 | definitive_source |
quote |
bc2 alcohol |
| 2 | prefer_over_inference |
reserve |
bc3 crime_illegal |
| 3 | include_in_recommendations |
purchase |
bc4 death_tragedy_disaster |
| 4 | quote_policy_verbatim |
contact |
bc5 firearms_weapons_violence |
| 5 | prices_indicative |
hand_to_human |
bc6 gambling_betting |
| 6 | state_publish_date |
— | bc7 mental_health_self_harm |
| 7 | — | — | bc8 politics_elections |
| 8 | — | — | bc9 regulated_advice |
| 9 | — | — | bc10 tobacco_vaping_drugs |
The full key — each code’s number, name, plain-English meaning, bit and the schema it arrived in — is public at GET /v1/ai-policy-key, in @masterdb/shared and in both verifier libraries. Bit positions under an ai_policy_schema never change; a new toggle is a new ai_policy_schema that only appends, and a blocked-context code is never reused or renamed. If you meet an ai_policy_schema you do not know, refuse to act on the bits rather than guess — and fetch the record, whose named booleans are self-describing.
ai_policy_schema 1 has no blocked contexts: its blocked mask is 0. A business that has never sealed an AI policy is version 0 with every mask 0.
The blocked contexts
Section titled “The blocked contexts”A set blocked bit means: do not use this business’s data to build a response in that context. It is a suppression list the business chose, not a rating of the business — a toy shop may block bc1 adult_sexual and bc6 gambling_betting so that its products are never drawn into those conversations, while still being found for a birthday-present search. The ten contexts are those of the Business Portal’s AI policy screen, in a fixed order:
| Code | Name | The business does not want its data used to answer about |
|---|---|---|
bc1 |
adult_sexual |
adult and sexual content |
bc2 |
alcohol |
alcohol |
bc3 |
crime_illegal |
crime and illegal activity |
bc4 |
death_tragedy_disaster |
death, tragedy and disaster |
bc5 |
firearms_weapons_violence |
firearms, weapons and violence |
bc6 |
gambling_betting |
gambling and betting |
bc7 |
mental_health_self_harm |
mental health, self-harm and crisis |
bc8 |
politics_elections |
politics and elections |
bc9 |
regulated_advice |
regulated advice (medical, legal or financial) |
bc10 |
tobacco_vaping_drugs |
tobacco, vaping and recreational drugs |
Deciding whether a conversation is in one of these contexts is yours to do; the policy tells you what the business asked.
The AI policy, on a fetch
Section titled “The AI policy, on a fetch”A fetch carries the sealed record itself: ai_policy: {version, record, seal}, where record holds the named booleans — the ten blocked contexts in the nested blocked group — and seal is the business’s seal over them. A business that has never sealed an AI policy answers {version: 0, record: null, seal: null} and zero bits: nothing is affirmatively permitted beyond the AI-company Terms.
The sealed record is what proves a row’s bits: derive them from its exact bytes (the ai_policy.record member as served, never a re-serialisation) and compare. The verifier libraries do it (verifyAiPolicyBits / verify_ai_policy_bits), and so does POST /v1/verify when you send the record and its seal with the row’s ai_policy_bits. The one difference allowed is purchase, which MasterDB withholds on rows while the business’s authorised endpoints are suspended.
What the use and action names mean:
| Name | When true, the business says |
|---|---|
answer |
you may use this data to answer (on by default) |
quote |
you may state a price as the business’s current price (on by default) |
reserve |
you may make a booking or reservation on a person’s behalf |
purchase |
you may complete a purchase on a person’s behalf, through a checkout domain the business has authorised |
contact |
you may contact the business on a person’s behalf |
hand_to_human |
you must hand the person to the business for anything beyond answering (on by default) |
cite_as_source |
cite the business as the source |
definitive_source |
where its record conflicts with a third party, its record wins |
prefer_over_inference |
a field it left out means not stated, not unknown: do not infer or reconstruct it from other sources |
include_in_recommendations |
it may be included in recommendations |
quote_policy_verbatim |
quote its delivery, returns and warranty wording verbatim rather than paraphrase it |
prices_indicative |
its prices are indicative |
state_publish_date |
say when the information was last published, rather than implying it is current |
Action terms and blocked contexts are honoured by the counterparty — you. Nothing cryptographic can stop a system that ignores purchase: false or a blocked context; that is a breach of the AI-company Terms, and the receipts and the evidence they give are the remedy.
An AI policy record of the earlier form reads {"schema": "masterdb/terms/1", "terms_schema": 1, …}: the same use and action booleans, no blocked contexts. It is served and verified exactly as sealed.
Which version governs
Section titled “Which version governs”The AI policy that governs a delivery is the business’s sealed policy in force at served_at, and the receipt names the version applied to each row (ai_policy_version). If a business tightens its policy and a region has not yet received the change, the version on your receipt governs: a lag is MasterDB’s incident, never your breach.
The AI-company Terms
Section titled “The AI-company Terms”GET /v1/terms answers the Terms in force — version, effective_from, sha256 and the text — and GET /v1/terms/{version} any earlier version, so the version a receipt names can always be read in the words that governed it.