{"id":50975332,"url":"https://github.com/prefix-dev/siglog","last_synced_at":"2026-06-19T07:02:11.903Z","repository":{"id":336300885,"uuid":"1138280418","full_name":"prefix-dev/siglog","owner":"prefix-dev","description":"Rust implementation of a transparency log: Merkle tree backed by OpenDAL (FS, S3, ...) and sea-orm database (SQLite, Postgres, ...)","archived":false,"fork":false,"pushed_at":"2026-04-30T15:49:26.000Z","size":384,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-19T04:00:10.271Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/prefix-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-20T13:23:57.000Z","updated_at":"2026-04-30T15:52:51.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/prefix-dev/siglog","commit_stats":null,"previous_names":["prefix-dev/siglog"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/prefix-dev/siglog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prefix-dev%2Fsiglog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prefix-dev%2Fsiglog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prefix-dev%2Fsiglog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prefix-dev%2Fsiglog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/prefix-dev","download_url":"https://codeload.github.com/prefix-dev/siglog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/prefix-dev%2Fsiglog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34516549,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-19T02:00:06.005Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-19T07:02:10.740Z","updated_at":"2026-06-19T07:02:11.889Z","avatar_url":"https://github.com/prefix-dev.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Siglog Banner](https://github.com/user-attachments/assets/dea9a3a6-94fd-45ee-a123-ccc126e88163)\n\n# siglog\n\nA Rust implementation of a [Tessera](https://github.com/transparency-dev/tessera)-compatible transparency log server for package distribution systems.\n\nThis implementation follows the [C2SP tlog-tiles](https://c2sp.org/tlog-tiles) specification and provides an append-only Merkle tree with cryptographic guarantees that all users see the same data.\n\n## Features\n\n- **Transparency Log Server**: Accepts entries, builds a Merkle tree, and publishes signed checkpoints\n- **Witness Server**: Independent co-signing of checkpoints following the [tlog-witness](https://c2sp.org/tlog-witness) specification\n- **Verifiable Index**: Optional key-value index with cryptographic proofs for efficient lookups\n- **Multiple Storage Backends**: S3-compatible storage (Tigris, MinIO) or local filesystem\n- **Multiple Database Backends**: SQLite (with LiteFS for distribution) or PostgreSQL\n\n## Architecture\n\n```\n┌─────────────┐\n│   Client    │\n└──────┬──────┘\n       │\n  ┌────┼────┐\n  │    │    │\n  ▼    ▼    ▼\n┌──────────┐ ┌─────────┐\n│Log Server│ │ Witness │\n│   8080   │ │  8081   │\n└────┬─────┘ └────┬────┘\n     │            │\n     └────────────┘\n            │\n            ▼\n    ┌──────────────────┐\n    │  SQLite (LiteFS) │\n    │  or PostgreSQL   │\n    └────────┬─────────┘\n             │\n             ▼\n    ┌──────────────────┐\n    │ S3 / Filesystem  │\n    │  (Tile Storage)  │\n    └──────────────────┘\n```\n\n## Building\n\n### Prerequisites\n\n- Rust 1.75+ (install via [rustup](https://rustup.rs/))\n- SQLite 3.x or PostgreSQL 14+\n- (Optional) S3-compatible storage for production\n\n### Build\n\n```bash\n# Build all binaries in release mode\ncargo build --release\n\n# Binaries will be in ./target/release/\n# - siglog           (log server)\n# - witness          (witness server)\n```\n\n## Configuration\n\n### Environment Variables\n\n#### Log Server (`siglog`)\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `LISTEN_ADDR` | Server listen address | `0.0.0.0:8080` |\n| `DATABASE_URL` | Database connection string | `sqlite:./siglog.db?mode=rwc` |\n| `LOG_ORIGIN` | Log origin identifier | Required |\n| `LOG_PRIVATE_KEY` | Ed25519 signing key (note format) | Required |\n| `STORAGE_BACKEND` | Storage type: `s3` or `fs` | `fs` |\n| `FS_ROOT` | Filesystem storage path (`STORAGE_PATH` is also accepted for compatibility) | `./tiles` |\n| `S3_BUCKET` | S3 bucket name | - |\n| `S3_ACCESS_KEY` | S3 access key | - |\n| `S3_SECRET_KEY` | S3 secret key | - |\n| `S3_ENDPOINT` | S3 endpoint URL | - |\n| `S3_REGION` | S3 region | `auto` |\n| `API_KEY` | Bearer token required for `/add` writes | Required unless `ALLOW_PUBLIC_WRITES=true` |\n| `ALLOW_PUBLIC_WRITES` | Allow unauthenticated `/add` writes for local development | `false` |\n| `CHECKPOINT_INTERVAL` | Checkpoint frequency (seconds) | `1` |\n| `BATCH_MAX_SIZE` | Max entries per batch | `256` |\n| `BATCH_MAX_AGE_MS` | Max batch age (ms) | `1000` |\n| `VINDEX_ENABLED` | Enable verifiable index | `false` |\n| `VINDEX_KEY_FIELD` | JSON field for key extraction | `name` |\n| `VINDEX_WAL_PATH` | WAL path for persistent vindex recovery | Required when enabling vindex on a non-empty log |\n\n#### Witness Server (`witness`)\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| `LISTEN_ADDR` | Server listen address | `0.0.0.0:8081` |\n| `DATABASE_URL` | Database connection string | `sqlite:./witness.db` |\n| `WITNESS_PRIVATE_KEY` | Ed25519 signing key (note format) | Required |\n| `WITNESS_LOGS` | Logs to witness (format: `origin=vkey`) | Required |\n\n### Key Format\n\nKeys use the [note signature format](https://pkg.go.dev/golang.org/x/mod/sumdb/note):\n\n```\n# Private key format:\nPRIVATE+KEY+\u003cname\u003e+\u003ckey_id\u003e+\u003cbase64_seed\u003e\n\n# Public key (verification key) format:\n\u003cname\u003e+\u003ckey_id\u003e+\u003cbase64_pubkey\u003e\n\n# Example:\nPRIVATE+KEY+example.com/log+a1b2c3d4+SGVsbG8gV29ybGQh...\nexample.com/log+a1b2c3d4+SGVsbG8gV29ybGQh...\n```\n\nGenerate a new keypair:\n\n```python\n#!/usr/bin/env python3\nimport base64\nimport hashlib\nfrom cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey\n\nname = \"example.com/log\"\nprivate_key = Ed25519PrivateKey.generate()\npublic_key = private_key.public_key()\n\nseed = private_key.private_bytes_raw()\npub_bytes = public_key.public_bytes_raw()\n\n# Key ID is first 4 bytes of SHA256(name || 0x0a || 0x01 || pubkey)\nh = hashlib.sha256(name.encode() + b\"\\n\\x01\" + pub_bytes).digest()\nkey_id = base64.b64encode(h[:4]).decode().rstrip(\"=\")\n\nprivate_note = f\"PRIVATE+KEY+{name}+{key_id}+{base64.b64encode(b'\\\\x01' + seed).decode()}\"\npublic_note = f\"{name}+{key_id}+{base64.b64encode(b'\\\\x01' + pub_bytes).decode()}\"\n\nprint(f\"Private: {private_note}\")\nprint(f\"Public:  {public_note}\")\n```\n\n## Running Locally\n\n### Using Docker Compose\n\nThe easiest way to run locally is with Docker Compose:\n\n```bash\n# Create a .env file with LOG_PRIVATE_KEY, LOG_PUBLIC_KEY,\n# WITNESS_PRIVATE_KEY, and MONITOR_PRIVATE_KEY.\n\n# Build and start services\ndocker compose -f docker/docker-compose.yml build\ndocker compose -f docker/docker-compose.yml up\n```\n\nThis starts:\n- Log server on `http://localhost:8080`\n- Witness server on `http://localhost:8081`\n\n### Running Manually\n\n```bash\n# Start the log server\nexport LOG_ORIGIN=\"my-transparency-log\"\nexport LOG_PRIVATE_KEY=\"PRIVATE+KEY+my-transparency-log+xxxx+...\"\nexport DATABASE_URL=\"sqlite:./siglog.db\"\nexport STORAGE_BACKEND=\"fs\"\nexport FS_ROOT=\"./tiles\"\nexport API_KEY=\"local-dev-token\"\n\n./target/release/siglog\n\n# In another terminal, start the witness\nexport WITNESS_PRIVATE_KEY=\"PRIVATE+KEY+witness.example.com+xxxx+...\"\nexport WITNESS_LOGS=\"my-transparency-log=my-transparency-log+xxxx+...\"\nexport DATABASE_URL=\"sqlite:./witness.db\"\n\n./target/release/witness\n```\n\n## Running a Witness\n\nA witness independently verifies and co-signs transparency log checkpoints. Running a witness helps ensure the log operator cannot present different views to different users.\n\n### Standalone Witness\n\n```bash\n./target/release/witness \\\n    --database-url sqlite:./witness.db \\\n    --private-key \"PRIVATE+KEY+witness.example.com+xxxx+base64...\" \\\n    --log \"log.example.com=log.example.com+yyyy+base64pubkey...\" \\\n    --listen 0.0.0.0:8081\n```\n\n### Witness API\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/add-checkpoint` | POST | Submit a checkpoint for co-signing |\n| `/health` | GET | Health check |\n| `/ready` | GET | Readiness check |\n\n#### POST /add-checkpoint\n\nRequest body:\n```json\n{\n  \"checkpoint\": \"log.example.com\\n123\\nROOTHASH...\\n\\n- log.example.com SIGNATURE...\",\n  \"proof\": [\"HASH1...\", \"HASH2...\"],\n  \"old_size\": 100\n}\n```\n\nResponse (on success): The witness's cosignature line.\n\n## API Reference\n\n### Log Server Endpoints\n\n| Endpoint | Method | Description |\n|----------|--------|-------------|\n| `/add` | POST | Add a new entry to the log |\n| `/checkpoint` | GET | Get the latest signed checkpoint |\n| `/tile/{level}/{index}` | GET | Get a Merkle tree tile |\n| `/tile/entries/{index}` | GET | Get an entry bundle |\n| `/health` | GET | Health check |\n| `/ready` | GET | Readiness check |\n\n### Add Entry\n\n```bash\ncurl -X POST http://localhost:8080/add \\\n  -H \"Authorization: Bearer local-dev-token\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\": \"my-package\", \"version\": \"1.0.0\", \"sha256\": \"abc123...\"}'\n```\n\n### Get Checkpoint\n\n```bash\ncurl http://localhost:8080/checkpoint\n```\n\nResponse:\n```\nmy-transparency-log\n42\nYnl0ZXMgb2YgdGhlIHJvb3QgaGFzaA==\n\n— my-transparency-log Ab1CdEf...\n```\n\n### Get Merkle Tile\n\n```bash\n# Get tile at level 0, index 0\ncurl http://localhost:8080/tile/0/000\n```\n\n### Get Entries\n\n```bash\n# Get entry bundle at index 0 (entries 0-255)\ncurl http://localhost:8080/tile/entries/000\n```\n\n## Deployment\n\n### Fly.io\n\nQuick start:\n```bash\n# Create app and storage\nfly apps create my-siglog\nfly storage create\nfly consul attach\nfly volumes create litefs --size 1\n\n# Set secrets\nfly secrets set LOG_PRIVATE_KEY=\"PRIVATE+KEY+...\"\nfly secrets set API_KEY=\"...\"\nfly secrets set S3_ACCESS_KEY=\"...\" S3_SECRET_KEY=\"...\" S3_BUCKET=\"...\"\n\n# Deploy\nfly deploy\n```\n\n### Docker\n\nPre-built images are available from GitHub Container Registry:\n\n```bash\n# Log server (replace OWNER/REPO with your GitHub repository)\ndocker pull ghcr.io/OWNER/REPO-server:latest\n\n# Witness\ndocker pull ghcr.io/OWNER/REPO-witness:latest\n```\n\nRun the log server:\n\n```bash\ndocker run -d \\\n  -p 8080:8080 \\\n  -v siglog-data:/data \\\n  -e LOG_ORIGIN=\"my-transparency-log\" \\\n  -e LOG_PRIVATE_KEY=\"PRIVATE+KEY+...\" \\\n  -e API_KEY=\"...\" \\\n  ghcr.io/OWNER/REPO-server:latest\n```\n\nRun the witness:\n\n```bash\ndocker run -d \\\n  -p 8081:8081 \\\n  -v witness-data:/data \\\n  -e WITNESS_PRIVATE_KEY=\"PRIVATE+KEY+...\" \\\n  -e WITNESS_LOGS=\"my-transparency-log=my-transparency-log+xxxx+...\" \\\n  ghcr.io/OWNER/REPO-witness:latest\n```\n\nTo build images locally:\n\n```bash\ndocker build -f docker/Dockerfile.server -t siglog-server .\ndocker build -f docker/Dockerfile.witness -t siglog-witness .\n```\n\n### Kubernetes\n\nExample deployment manifest:\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: transparency-log\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: transparency-log\n  template:\n    metadata:\n      labels:\n        app: transparency-log\n    spec:\n      containers:\n      - name: log\n        image: your-registry/siglog:latest\n        ports:\n        - containerPort: 8080\n        env:\n        - name: LOG_ORIGIN\n          value: \"your-log.example.com\"\n        - name: LOG_PRIVATE_KEY\n          valueFrom:\n            secretKeyRef:\n              name: siglog-secrets\n              key: log-private-key\n        - name: API_KEY\n          valueFrom:\n            secretKeyRef:\n              name: siglog-secrets\n              key: api-key\n        - name: DATABASE_URL\n          value: \"postgres://user:pass@postgres:5432/siglog\"\n        - name: STORAGE_BACKEND\n          value: \"s3\"\n        envFrom:\n        - secretRef:\n            name: s3-credentials\n```\n\n## Verification\n\nClients can verify entries against the transparency log:\n\n1. Fetch the latest checkpoint\n2. Verify the checkpoint signature\n3. Verify any witness cosignatures\n4. For a specific entry, fetch the inclusion proof\n5. Verify the proof against the checkpoint root hash\n\n## License\n\nBSD-3-Clause. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprefix-dev%2Fsiglog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fprefix-dev%2Fsiglog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fprefix-dev%2Fsiglog/lists"}