siglog
A Rust implementation of a Tessera-compatible transparency log server for package distribution systems.
This implementation follows the C2SP tlog-tiles specification and provides an append-only Merkle tree with cryptographic guarantees that all users see the same data.
Features
- Transparency Log Server: Accepts entries, builds a Merkle tree, and publishes signed checkpoints
- Witness Server: Independent co-signing of checkpoints following the tlog-witness specification
- Verifiable Index: Optional key-value index with cryptographic proofs for efficient lookups
- Multiple Storage Backends: S3-compatible storage (Tigris, MinIO) or local filesystem
- Multiple Database Backends: SQLite (with LiteFS for distribution) or PostgreSQL
Architecture
┌─────────────┐
│ Client │
└──────┬──────┘
│
┌────┼────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌─────────┐
│Log Server│ │ Witness │
│ 8080 │ │ 8081 │
└────┬─────┘ └────┬────┘
│ │
└────────────┘
│
▼
┌──────────────────┐
│ SQLite (LiteFS) │
│ or PostgreSQL │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ S3 / Filesystem │
│ (Tile Storage) │
└──────────────────┘
Building
Prerequisites
- Rust 1.75+ (install via rustup)
- SQLite 3.x or PostgreSQL 14+
- (Optional) S3-compatible storage for production
Build
# Build all binaries in release mode
# Binaries will be in ./target/release/
# - siglog (log server)
# - witness (witness server)
Configuration
Environment Variables
Log Server (siglog)
| Variable | Description | Default |
|---|---|---|
LISTEN_ADDR |
Server listen address | 0.0.0.0:8080 |
DATABASE_URL |
Database connection string | sqlite:./siglog.db |
LOG_ORIGIN |
Log origin identifier | transparency-log |
LOG_PRIVATE_KEY |
Ed25519 signing key (note format) | Required |
STORAGE_BACKEND |
Storage type: s3 or fs |
fs |
STORAGE_PATH |
Filesystem storage path | ./tiles |
S3_BUCKET |
S3 bucket name | - |
S3_ACCESS_KEY |
S3 access key | - |
S3_SECRET_KEY |
S3 secret key | - |
S3_ENDPOINT |
S3 endpoint URL | - |
S3_REGION |
S3 region | auto |
CHECKPOINT_INTERVAL |
Checkpoint frequency (seconds) | 5 |
BATCH_MAX_SIZE |
Max entries per batch | 256 |
BATCH_MAX_AGE_MS |
Max batch age (ms) | 2000 |
VINDEX_ENABLED |
Enable verifiable index | false |
VINDEX_KEY_FIELD |
JSON field for key extraction | name |
Witness Server (witness)
| Variable | Description | Default |
|---|---|---|
LISTEN_ADDR |
Server listen address | 0.0.0.0:8081 |
DATABASE_URL |
Database connection string | sqlite:./witness.db |
WITNESS_PRIVATE_KEY |
Ed25519 signing key (note format) | Required |
WITNESS_LOGS |
Logs to witness (format: origin=vkey) |
Required |
Key Format
Keys use the note signature format:
# Private key format:
PRIVATE+KEY+<name>+<key_id>+<base64_seed>
# Public key (verification key) format:
<name>+<key_id>+<base64_pubkey>
# Example:
PRIVATE+KEY+example.com/log+a1b2c3d4+SGVsbG8gV29ybGQh...
example.com/log+a1b2c3d4+SGVsbG8gV29ybGQh...
Generate a new keypair:
#!/usr/bin/env python3
=
=
=
=
=
# Key ID is first 4 bytes of SHA256(name || 0x0a || 0x01 || pubkey)
=
=
= f
= f
Running Locally
Using Docker Compose
The easiest way to run locally is with Docker Compose:
# Generate keys and create .env file
# Build and start services
This starts:
- Log server on
http://localhost:8080 - Witness server on
http://localhost:8081
Running Manually
# Start the log server
# In another terminal, start the witness
Running a Witness
A witness independently verifies and co-signs transparency log checkpoints. Running a witness helps ensure the log operator cannot present different views to different users.
Standalone Witness
Witness API
| Endpoint | Method | Description |
|---|---|---|
/add-checkpoint |
POST | Submit a checkpoint for co-signing |
/health |
GET | Health check |
POST /add-checkpoint
Request body:
Response (on success): The witness's cosignature line.
API Reference
Log Server Endpoints
| Endpoint | Method | Description |
|---|---|---|
/add |
POST | Add a new entry to the log |
/checkpoint |
GET | Get the latest signed checkpoint |
/tile/{level}/{index} |
GET | Get a Merkle tree tile |
/tile/entries/{index} |
GET | Get an entry bundle |
/health |
GET | Health check |
Add Entry
Get Checkpoint
Response:
my-transparency-log
42
Ynl0ZXMgb2YgdGhlIHJvb3QgaGFzaA==
— my-transparency-log Ab1CdEf...
Get Merkle Tile
# Get tile at level 0, index 0
Get Entries
# Get entry bundle at index 0 (entries 0-255)
Deployment
Fly.io
See DEPLOY.md for detailed Fly.io deployment instructions using LiteFS and Tigris.
Quick start:
# Create app and storage
# Set secrets
# Deploy
Docker
Pre-built images are available from GitHub Container Registry:
# Log server (replace OWNER/REPO with your GitHub repository)
# Witness
Run the log server:
Run the witness:
To build images locally:
Kubernetes
Example deployment manifest:
apiVersion: apps/v1
kind: Deployment
metadata:
name: transparency-log
spec:
replicas: 1
selector:
matchLabels:
app: transparency-log
template:
metadata:
labels:
app: transparency-log
spec:
containers:
- name: log
image: your-registry/siglog:latest
ports:
- containerPort: 8080
env:
- name: LOG_ORIGIN
value: "your-log.example.com"
- name: LOG_PRIVATE_KEY
valueFrom:
secretKeyRef:
name: siglog-secrets
key: log-private-key
- name: DATABASE_URL
value: "postgres://user:pass@postgres:5432/siglog"
- name: STORAGE_BACKEND
value: "s3"
envFrom:
- secretRef:
name: s3-credentials
Verification
Clients can verify entries against the transparency log:
- Fetch the latest checkpoint
- Verify the checkpoint signature
- Verify any witness cosignatures
- For a specific entry, fetch the inclusion proof
- Verify the proof against the checkpoint root hash
License
BSD-3-Clause. See LICENSE for details.