{
  "markdown": "# Mark\n\n[![npm version](https://img.shields.io/npm/v/@tatastu/mark.svg)](https://www.npmjs.com/package/@tatastu/mark)\n[![CI](https://github.com/Tatastu-Labs/mark/actions/workflows/ci.yml/badge.svg)](https://github.com/Tatastu-Labs/mark/actions/workflows/ci.yml)\n[![service status](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fmark.tatastu.dev%2Fhealth&query=%24.ok&label=service&color=success&logo=cloudflare)](https://mark.tatastu.dev/health)\n[![license](https://img.shields.io/npm/l/@tatastu/mark.svg)](LICENSE)\n\n**A working answer to EU AI Act Article 50.** Article 50(4) requires providers of\ngeneral-purpose AI systems to mark AI-generated content with machine-readable\nprovenance metadata by **2 August 2026**. Mark is one API call: hash the\ncontent locally, get back a permanent, publicly verifiable record of who signed it and\nwhen. Works from any language, any agent, any pipeline. (This is not legal advice;\nwhether a given stamp satisfies your specific Article 50 obligation depends on your use\ncase and counsel, see [docs/eu-ai-act.md](docs/eu-ai-act.md).)\n\n```ts\nimport { stamp, hashText } from \"@tatastu/mark\"\n\nconst hash = await hashText(\"Hello, world!\")\nconst receipt = await stamp({ contentHash: hash, title: \"My post\", apiKey: process.env.TATASTU_API_KEY })\nconsole.log(receipt.verifyUrl)\n// → https://mark.tatastu.dev/p/prf_01jz...\n```\n\nLive service: [mark.tatastu.dev](https://mark.tatastu.dev) — verification is always free.\nOpen the printed `verifyUrl` in a browser to see the public record: signer, timestamp,\nand the Arweave/Base anchor once it lands.\n\n**For people:** sign in at [mark.tatastu.dev/account](https://mark.tatastu.dev/account),\nchoose a file, and make the lifetime sample stamp without creating an API key. The file is\nhashed in the browser and never uploaded. API keys are portable credentials for agents,\nscripts, and other applications.\n\n---\n\n## What it does\n\nA **stamp** takes the SHA-256 of any content (text, file, JSON, code, binary) and records:\n\n- Who signed it (a creator identity you provide, or anonymous)\n- When it was signed (millisecond-precision, bounded by the daily Merkle anchor)\n- An Ed25519 signature from the Mark service's key-transparency log\n\nWithin 24 hours, stamps are batched into an RFC 6962 Merkle tree. The root is anchored\nto **Arweave** (permanent storage) and **Base** (EVM on-chain calldata). After anchoring,\nyou can verify the stamp with zero network calls using the offline verifier in this repo.\n\nWhat a stamp **does not** prove: authorship truth. The service records \"this signer\nclaimed this content at this time.\" The claimed time (`signedAt`) and proven time\n(anchored) are distinct and both shown on every verify page.\n\n---\n\n## Install\n\n```bash\nnpm install @tatastu/mark\n```\n\nOr use without installing:\n\n```bash\nnpx tsx examples/eu-ai-act-label.ts\n```\n\n---\n\n## Quickstart\n\n### Stamp text\n\n```ts\nimport { stamp, hashText } from \"@tatastu/mark\"\n\nconst hash = await hashText(\"The report content goes here.\")\nconst receipt = await stamp({ contentHash: hash, title: \"Q3 Report\", apiKey: process.env.TATASTU_API_KEY })\nconsole.log(receipt.verifyUrl)   // https://mark.tatastu.dev/p/prf_...\nconsole.log(receipt.byline)      // \"Verified · https://mark.tatastu.dev/p/prf_...\"\n```\n\n### Stamp a file (Node.js)\n\n```ts\nimport { stamp, hashNodeBuffer } from \"@tatastu/mark\"\nimport { readFile } from \"node:fs/promises\"\n\nconst bytes = await readFile(\"./report.pdf\")\nconst receipt = await stamp({\n  contentHash: await hashNodeBuffer(bytes),\n  contentType: \"application/pdf\",\n  title: \"Q3 Financial Report\",\n  apiKey: process.env.TATASTU_API_KEY,\n})\n```\n\n### Verify any content\n\n```ts\nimport { verify, hashText } from \"@tatastu/mark\"\n\nconst { proofs } = await verify(await hashText(\"The report content goes here.\"))\nif (proofs.length > 0) {\n  console.log(\"Authentic:\", proofs[0].verifyUrl)\n  console.log(\"Status:\", proofs[0].status)   // SIGNED | ANCHORED | CONFIRMED\n}\n```\n\n### Stamp a file in the browser (drag-and-drop)\n\n```ts\nimport { stamp, hashBlob } from \"@tatastu/mark\"\n\nconst file = dropEvent.dataTransfer.files[0]\nconst receipt = await stamp({\n  contentHash: await hashBlob(file),\n  title: file.name,\n})\n```\n\nSee [examples/browser-drop.html](examples/browser-drop.html) for a complete\nself-contained verify page with no build step.\n\n---\n\n## MCP (agent use)\n\nAdd `mark.tatastu.dev/mcp` to your Claude Code, Cursor, or Windsurf config:\n\n```json\n{\n  \"mcpServers\": {\n    \"tatastu-proof\": {\n      \"url\": \"https://mark.tatastu.dev/mcp\"\n    }\n  }\n}\n```\n\nThe agent can then call `create_proof` and `verify_proof` directly. See\n[examples/agent-mcp.md](examples/agent-mcp.md) for the full config and tool reference.\n\n---\n\n## EU AI Act compliance\n\nEU AI Act Article 50(4) requires machine-readable provenance on AI-generated content.\nThe deadline is 2 August 2026.\n\n```ts\nimport { stamp, hashText } from \"@tatastu/mark\"\n\nconst aiOutput = \"AI-generated text goes here.\"\nconst receipt = await stamp({ contentHash: await hashText(aiOutput) })\n\nconst labeledOutput = {\n  text: aiOutput,\n  _proof: {\n    contentHash: receipt.contentHash,\n    proofId: receipt.proofId,\n    verifyUrl: receipt.verifyUrl,\n    signedAt: receipt.signedAt,\n    bylineHtml: receipt.bylineHtml,\n  },\n}\n```\n\nRun the full example with no setup:\n\n```bash\nnpx tsx examples/eu-ai-act-label.ts\n```\n\nSee [docs/eu-ai-act.md](docs/eu-ai-act.md) for the compliance guide.\n\n---\n\n## How verification works\n\nEvery stamp is:\n\n1. **Signed** immediately with an Ed25519 key from the service's transparency log\n2. **Batched** daily into an RFC 6962 Merkle tree (0x00/0x01 domain-separated prefixes)\n3. **Anchored** — the Merkle root written to Arweave (permanent) and Base (on-chain calldata)\n4. **Confirmed** once the Arweave transaction has sufficient confirmations\n\nAfter anchoring you can verify with the public key and inclusion path — no network,\nno trust, no service required.\n\n### Four verified layers on every verify page\n\n1. **Ed25519 signature** — the service signed the canonical receipt\n2. **RFC 6962 Merkle inclusion** — the stamp is in the anchored batch\n3. **Arweave anchor** — the Merkle root is on Arweave\n4. **Base calldata** — the root is in an EVM transaction on Base\n\n---\n\n## Offline verification\n\nAfter a stamp reaches `ANCHORED` status, you can verify it with no network calls:\n\n```ts\nimport { getBundle, hashNodeBuffer } from \"@tatastu/mark\"\nimport { verifyOffline } from \"@tatastu/mark/verify/offline\"\nimport { readFile } from \"node:fs/promises\"\n\nconst bytes = await readFile(\"./report.pdf\")\nconst bundle = await getBundle(\"prf_01jz...\")\nconst result = await verifyOffline(bundle, await hashNodeBuffer(bytes))\nconsole.log(result.valid, result.signatureVerified, result.merkleVerified)\n```\n\n`verify/offline.ts` is a single 200-line file with zero dependencies. Copy it into\nany project. It uses only the Web Crypto API (SubtleCrypto), available in Node 18+,\nDeno, and modern browsers.\n\n---\n\n## Pricing\n\n| Tier | Price | Stamps | Status |\n|------|-------|--------|--------|\n| First stamp | $0 | 1 lifetime sample per durable account | Live |\n| Tatastu membership | $20/month (the whole [Tatastu app](https://tatastu.dev)) | 100/month included | Live |\n| Prepaid packs | $1 / $1.50 / $5 / $8 | 5 / 10 / 50 / 100 (no expiry) | Live |\n| Volume packs | $35 / $65 / $120 | 500 / 1,000 / 2,000 (no expiry) | Live |\n| Pay-per-stamp (x402) | $0.10, or $0.05 with API key | one stamp | Live |\n\n**Verification is always free and requires no account.**\n\nThe checkout page reveals only packs that the live offer manifest reports as available.\nFull, always-current pricing:\n[mark.tatastu.dev/pricing](https://mark.tatastu.dev/pricing) (mirrored in\n[docs/pricing.md](docs/pricing.md)).\n\n---\n\n## Integrations and examples\n\n| Framework / target | File |\n|---|---|\n| LangChain (tool-calling agent) | [examples/langchain-tool.ts](examples/langchain-tool.ts) |\n| Vercel AI SDK | [examples/vercel-ai-tool.ts](examples/vercel-ai-tool.ts) |\n| CrewAI / AutoGen (Python) | [examples/crewai-autogen-tool.py](examples/crewai-autogen-tool.py) |\n| MCP (Claude Code, Cursor, Windsurf) | [examples/agent-mcp.md](examples/agent-mcp.md) |\n| ChatGPT Actions (OpenAPI 3.1) | hosted at [mark.tatastu.dev/.well-known/gpt-actions.json](https://mark.tatastu.dev/.well-known/gpt-actions.json), source: [examples/gpt-actions.json](examples/gpt-actions.json) |\n| Browser, no build step | [examples/browser-drop.html](examples/browser-drop.html) |\n| Node.js CLI | [examples/node-stamp.ts](examples/node-stamp.ts) |\n| EU AI Act labeling | [examples/eu-ai-act-label.ts](examples/eu-ai-act-label.ts) |\n\nFull REST reference (every endpoint, error table, rate limits): [docs/api.md](docs/api.md).\n\n---\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md). Found a security issue? See\n[SECURITY.md](SECURITY.md) instead of opening a public issue. The service, Worker, and D1 schema are in a\nprivate repo — this repo contains only the public SDK, offline verifier, and examples.\n",
  "bytes": 8990,
  "sha": "025eede06997f17bde574e6d7e631071fa83dae1cfe4f15bd2254506a420845b",
  "repo_slug": "tatastu-labs/proof",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_dev_tatastu_proof_28e5e51f/readme"
}