Proof-of-Play.
Cryptographic ad verification with Ed25519 signed proofs
How It Works
Every ad impression on Trillboards is cryptographically signed. This creates an immutable, independently verifiable proof that an ad actually played on a specific screen.
Ad Plays on Screen
When an ad plays, the SDK captures impression details β ad ID, screen ID, device ID, and a precise timestamp.
Cryptographic Signature
The impression payload is signed with Ed25519 using Trillboards' private key, creating a tamper-proof proof.
Open Verification
Anyone can verify the proof using the public key β no API access needed. The signature proves the ad actually played.
Open Verification, Closed Discovery
Trillboards uses an "Open Verification, Closed Discovery" model:
Open Verification
Anyone with a proof and the public key can verify it. No authentication needed. No API key required.
Closed Discovery
Listing proofs requires authentication. Only campaign owners and authorized partners can browse proof history.
Signature Format
All proofs use Ed25519 (version 2). The signed payload is constructed by concatenating fields with dots:
v2.<timestamp>.<adId>.<impressionId>.<screenId>.<deviceId>| Field | Type | Description |
|---|---|---|
version | string | "v2" β always the literal string |
timestamp | number | Unix epoch milliseconds when the ad played |
adId | string | Campaign or ad creative identifier |
impressionId | string | Unique impression ID |
screenId | string | Screen/location identifier |
deviceId | string | Physical device identifier |
The signature is prefixed with <code>ed25519=</code> followed by the hex-encoded signature bytes.
Verification Examples
Node.js
const crypto = require('crypto');
// Fetch from /v1/advertiser/proof/.well-known/public-key
const PUBLIC_KEY_HEX = 'a5a9c43785680c62ebcd01ec49a0d6055fd140023df0f142c3e37c6abafce34f';
function verifyProof(proof) {
const payload = `v2.${proof.timestamp}.${proof.adId}.${proof.impressionId}.${proof.screenId}.${proof.deviceId}`;
const signatureHex = proof.signature.replace('ed25519=', '');
const signatureBytes = Buffer.from(signatureHex, 'hex');
const publicKey = crypto.createPublicKey({
key: Buffer.concat([
Buffer.from('302a300506032b6570032100', 'hex'),
Buffer.from(PUBLIC_KEY_HEX, 'hex')
]),
format: 'der',
type: 'spki'
});
return crypto.verify(null, Buffer.from(payload), publicKey, signatureBytes);
}
const proof = {
impressionId: '507f1f77bcf86cd799439011',
adId: 'campaign-abc123',
screenId: 'screen-xyz789',
deviceId: 'device-456',
timestamp: 1705401600000,
signature: 'ed25519=a1b2c3d4e5f6...'
};
console.log('Valid:', verifyProof(proof));Python
from nacl.signing import VerifyKey
from nacl.exceptions import BadSignature
PUBLIC_KEY_HEX = 'a5a9c43785680c62ebcd01ec49a0d6055fd140023df0f142c3e37c6abafce34f'
def verify_proof(proof):
verify_key = VerifyKey(bytes.fromhex(PUBLIC_KEY_HEX))
payload = f"v2.{proof['timestamp']}.{proof['adId']}.{proof['impressionId']}.{proof['screenId']}.{proof['deviceId']}"
signature_hex = proof['signature'].replace('ed25519=', '')
signature_bytes = bytes.fromhex(signature_hex)
try:
verify_key.verify(payload.encode(), signature_bytes)
return True
except BadSignature:
return FalsecURL
# Verify a proof using the API (no auth required)
curl -X POST https://api.trillboards.com/v1/advertiser/proof/verify-proof \
-H "Content-Type: application/json" \
-d '{
"signature": "ed25519=a1b2c3d4e5f6...",
"timestamp": 1705401600000,
"adId": "campaign-abc123",
"impressionId": "507f1f77bcf86cd799439011",
"screenId": "screen-xyz789",
"deviceId": "device-456"
}'
# Response:
# {
# "success": true,
# "verification": {
# "valid": true,
# "version": "v2"
# }
# }API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /v1/advertiser/proof/.well-known/public-key | None | Returns the Ed25519 public key used to sign all proofs. |
| POST | /v1/advertiser/proof/verify-proof | None | Submit a proof for server-side verification. Returns validity and version. |
| GET | /v1/advertiser/proof/proofs | Bearer token | List proofs for your campaigns with pagination. Query params: campaign_id, start_date, end_date, limit, offset. |
| GET | /v1/advertiser/proof/proofs/:id | Bearer token | Get a single proof by ID with full details. |
Why It Matters
Tamper-Proof
Ed25519 signatures are impossible to forge. Every proof is mathematically verifiable.
Open Verification
No API key needed to verify. Anyone can confirm an ad played using the public key.
Closed Discovery
Only authenticated partners can list their proofs. You control who sees your impression data.
Unique Per-Impression
Every single impression gets its own unique signature. No batching, no aggregation, no approximation.