mirror of
https://github.com/btouchard/ackify-ce.git
synced 2026-02-08 14:58:36 -06:00
0ce076bd3ae0a5bea79c9f1bb4aaeb98a0847ecb
Move all template variables out of locale JSON files into separate keys. Update templates to concatenate locale strings with dynamic values. Replace inline conditionals with proper locale key lookups for pluralization.
🔐 Ackify
Proof of Read. Compliance made simple.
Secure document reading validation service with cryptographic traceability and irrefutable proof.
Visite our website here : https://www.ackify.eu
🎯 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/health # alias: /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
Log level setup
ACKIFY_LOG_LEVEL="info" # can be debug, info, warn(ing), error. default: info
🛡️ 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 64 | base64 -w 0)"
ACKIFY_ED25519_PRIVATE_KEY="$(openssl rand 64 | 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 /health- Health check (alias:/healthz)
Admin
GET /admin- Dashboard (restricted)GET /admin/docs/{docID}- Signatures for a documentGET /admin/api/chain-integrity/{docID}- Chain integrity JSON
Access control: set ACKIFY_ADMIN_EMAILS with a comma-separated list of admin emails (exact match, case-insensitive). Example:
ACKIFY_ADMIN_EMAILS="alice@company.com,bob@company.com"
🔍 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
# Optional: static analysis
go install honnef.co/go/tools/cmd/staticcheck@latest
staticcheck ./...
🤝 Support
Help & Documentation
- 🐛 Issues: GitHub Issues
- 💬 Discussions: GitHub Discussions
License (AGPLv3)
Distributed under the GNU Affero General Public License v3.0. See LICENSE for details.
Developed with ❤️ by Benjamin TOUCHARD
Languages
Go
59.5%
Vue
21%
TypeScript
12.1%
Shell
3.1%
PLpgSQL
1.9%
Other
2.3%





