mirror of
https://github.com/btouchard/ackify.git
synced 2026-01-04 20:09:57 -06:00
38e98a2e903254164fff36b3c2d8fb0e7ebb4dcf
🔐 Ackify
Proof of Read. Compliance made simple.
Secure document reading validation service with cryptographic traceability and irrefutable proof.
🎯 Why Ackify?
Problem: How to prove that a collaborator has actually read and understood an important document?
Solution: Ed25519 cryptographic signatures with immutable timestamps and complete traceability.
Real-world use cases
- ✅ Security policy validation
- ✅ Mandatory training attestations
- ✅ GDPR acknowledgment
- ✅ Contractual acknowledgments
- ✅ Quality and compliance procedures
📸 Vidéos
Click to GIFs for open videos WebM in your browser.
1) Create sign
|
2) User sign flow
|
📸 Screenshots
Home page
|
Signing request
|
Signature confirmed
|
Signatures list
|
Outline integration
|
Google Docs integration
|
⚡ Quick Start
With Docker (recommended)
git clone https://github.com/btouchard/ackify-ce.git
cd ackify-ce
# Minimal configuration
cp .env.example .env
# Edit .env with your OAuth2 settings
# Start
docker compose up -d
# Test
curl http://localhost:8080/healthz
Required variables
ACKIFY_BASE_URL="https://your-domain.com"
ACKIFY_OAUTH_CLIENT_ID="your-oauth-client-id" # Google/GitHub/GitLab
ACKIFY_OAUTH_CLIENT_SECRET="your-oauth-client-secret"
ACKIFY_DB_DSN="postgres://user:password@localhost/ackify?sslmode=disable"
ACKIFY_OAUTH_COOKIE_SECRET="$(openssl rand -base64 32)"
🚀 Simple Usage
1. Request a signature
https://your-domain.com/sign?doc=security_procedure_2025
→ User authenticates via OAuth2 and validates their reading
2. Verify signatures
# JSON API - Complete list
curl "https://your-domain.com/status?doc=security_procedure_2025"
# PNG Badge - Individual status
curl "https://your-domain.com/status.png?doc=security_procedure_2025&user=john.doe@company.com"
3. Integrate into your pages
<!-- Embeddable widget -->
<iframe src="https://your-domain.com/embed?doc=security_procedure_2025"
width="500" height="300"></iframe>
<!-- Via oEmbed -->
<script>
fetch('/oembed?url=https://your-domain.com/embed?doc=security_procedure_2025')
.then(r => r.json())
.then(data => document.getElementById('signatures').innerHTML = data.html);
</script>
🔧 OAuth2 Configuration
Supported providers
| Provider | Configuration |
|---|---|
ACKIFY_OAUTH_PROVIDER=google |
|
| GitHub | ACKIFY_OAUTH_PROVIDER=github |
| GitLab | ACKIFY_OAUTH_PROVIDER=gitlab + ACKIFY_OAUTH_GITLAB_URL |
| Custom | Custom endpoints |
Custom provider
# Leave ACKIFY_OAUTH_PROVIDER empty
ACKIFY_OAUTH_AUTH_URL="https://auth.company.com/oauth/authorize"
ACKIFY_OAUTH_TOKEN_URL="https://auth.company.com/oauth/token"
ACKIFY_OAUTH_USERINFO_URL="https://auth.company.com/api/user"
ACKIFY_OAUTH_SCOPES="read:user,user:email"
Domain restriction
ACKIFY_OAUTH_ALLOWED_DOMAIN="@company.com" # Only @company.com emails
🛡️ Security & Architecture
Cryptographic security
- Ed25519: State-of-the-art digital signatures
- SHA-256: Payload hashing against tampering
- Immutable timestamps: PostgreSQL triggers
- Encrypted sessions: Secure cookies
- CSP headers: XSS protection
Go architecture
cmd/ackapp/ # Entry point
internal/
domain/ # Business logic
models/ # Entities
repositories/ # Persistence interfaces
application/ # Use cases
services/ # Business implementations
infrastructure/ # Adapters
auth/ # OAuth2
database/ # PostgreSQL
config/ # Configuration
presentation/ # HTTP
handlers/ # Controllers + interfaces
templates/ # HTML views
pkg/ # Shared utilities
Technology stack
- Go 1.24.5: Performance and simplicity
- PostgreSQL: Integrity constraints
- OAuth2: Multi-provider
- Docker: Simplified deployment
- Traefik: HTTPS reverse proxy
📊 Database
CREATE TABLE signatures (
id BIGSERIAL PRIMARY KEY,
doc_id TEXT NOT NULL, -- Document ID
user_sub TEXT NOT NULL, -- OAuth user ID
user_email TEXT NOT NULL, -- User email
signed_at TIMESTAMPTZ NOT NULL, -- Signature timestamp
payload_hash TEXT NOT NULL, -- Cryptographic hash
signature TEXT NOT NULL, -- Ed25519 signature
nonce TEXT NOT NULL, -- Anti-replay
created_at TIMESTAMPTZ DEFAULT now(), -- Immutable
referer TEXT, -- Source (optional)
prev_hash TEXT,
UNIQUE (doc_id, user_sub) -- One signature per user/doc
);
Guarantees:
- ✅ Uniqueness: One user = one signature per document
- ✅ Immutability:
created_atprotected by trigger - ✅ Integrity: SHA-256 hash to detect modifications
- ✅ Non-repudiation: Ed25519 signature cryptographically provable
🚀 Production Deployment
docker-compose.yml
version: '3.8'
services:
ackapp:
image: btouchard/ackify-ce:latest
environment:
ACKIFY_BASE_URL: https://ackify.company.com
ACKIFY_DB_DSN: postgres://user:pass@postgres:5432/ackdb?sslmode=require
ACKIFY_OAUTH_CLIENT_ID: ${ACKIFY_OAUTH_CLIENT_ID}
ACKIFY_OAUTH_CLIENT_SECRET: ${ACKIFY_OAUTH_CLIENT_SECRET}
ACKIFY_OAUTH_COOKIE_SECRET: ${ACKIFY_OAUTH_COOKIE_SECRET}
labels:
- "traefik.enable=true"
- "traefik.http.routers.ackify.rule=Host(`ackify.company.com`)"
- "traefik.http.routers.ackify.tls.certresolver=letsencrypt"
postgres:
image: postgres:15-alpine
environment:
POSTGRES_DB: ackdb
POSTGRES_USER: ackuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
Production variables
# Enhanced security
ACKIFY_OAUTH_COOKIE_SECRET="$(openssl rand -base64 64)" # AES-256
ACKIFY_ED25519_PRIVATE_KEY="$(openssl genpkey -algorithm Ed25519 | base64 -w 0)"
# HTTPS mandatory
ACKIFY_BASE_URL="https://ackify.company.com"
# Secure PostgreSQL
ACKIFY_DB_DSN="postgres://user:pass@postgres:5432/ackdb?sslmode=require"
📋 Complete API
Authentication
GET /login?next=<url>- OAuth2 loginGET /logout- LogoutGET /oauth2/callback- OAuth2 callback
Signatures
GET /sign?doc=<id>- Signature interfacePOST /sign- Create signatureGET /signatures- My signatures (auth required)
Consultation
GET /status?doc=<id>- JSON all signaturesGET /status.png?doc=<id>&user=<email>- PNG badge
Integration
GET /oembed?url=<embed_url>- oEmbed metadataGET /embed?doc=<id>- HTML widget
Monitoring
GET /healthz- Health check
🔍 Development & Testing
Local build
# Dependencies
go mod tidy
# Build
go build ./cmd/community
# Linting
go fmt ./...
go vet ./...
# Tests (TODO: add tests)
go test -v ./...
Docker development
# Build image
docker build -t ackify-ce:dev .
# Run with local database
docker run -p 8080:8080 --env-file .env ackify-ce:dev
🤝 Support
Help & Documentation
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
SSPL License
Free usage for internal projects. Restriction for competing commercial services. See LICENSE for complete details.
Developed with ❤️ by Benjamin TOUCHARD
Languages
Go
67.6%
Vue
14.3%
TypeScript
10.8%
Shell
2.4%
PLpgSQL
2%
Other
2.9%





