# π Ackify
> **Proof of Read. Compliance made simple.**
Secure document reading validation service with cryptographic traceability and irrefutable proof.
[](https://github.com/btouchard/ackify-ce)
[](https://en.wikipedia.org/wiki/EdDSA)
[](https://golang.org/)
[](LICENSE)
> π«π· [Version franΓ§aise disponible ici](README_FR.md)
### 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)
```bash
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
```bash
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
```bash
# 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
```html
```
---
## π§ OAuth2 Configuration
### Supported providers
| Provider | Configuration |
|----------|---------------|
| **Google** | `ACKIFY_OAUTH_PROVIDER=google` |
| **GitHub** | `ACKIFY_OAUTH_PROVIDER=github` |
| **GitLab** | `ACKIFY_OAUTH_PROVIDER=gitlab` + `ACKIFY_OAUTH_GITLAB_URL` |
| **Custom** | Custom endpoints |
### Custom provider
```bash
# 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
```bash
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
```sql
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_at` protected by trigger
- β
**Integrity**: SHA-256 hash to detect modifications
- β
**Non-repudiation**: Ed25519 signature cryptographically provable
---
## π Production Deployment
### docker-compose.yml
```yaml
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
```bash
# 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=` - OAuth2 login
- `GET /logout` - Logout
- `GET /oauth2/callback` - OAuth2 callback
### Signatures
- `GET /sign?doc=` - Signature interface
- `POST /sign` - Create signature
- `GET /signatures` - My signatures (auth required)
### Consultation
- `GET /status?doc=` - JSON all signatures
- `GET /status.png?doc=&user=` - PNG badge
### Integration
- `GET /oembed?url=` - oEmbed metadata
- `GET /embed?doc=` - HTML widget
### Monitoring
- `GET /health` - Health check (alias: `/healthz`)
### Admin
- `GET /admin` - Dashboard (restricted)
- `GET /admin/docs/{docID}` - Signatures for a document
- `GET /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:
```bash
ACKIFY_ADMIN_EMAILS="alice@company.com,bob@company.com"
```
---
## π Development & Testing
### Local build
```bash
# Dependencies
go mod tidy
# Build
go build ./cmd/community
# Linting
go fmt ./...
go vet ./...
# Tests (TODO: add tests)
go test -v ./...
```
### Docker development
```bash
# 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](https://github.com/btouchard/ackify-ce/issues)
- π¬ **Discussions**: [GitHub Discussions](https://github.com/btouchard/ackify-ce/discussions)
### License (AGPLv3)
Distributed under the GNU Affero General Public License v3.0.
See [LICENSE](LICENSE) for details.
---
**Developed with β€οΈ by [Benjamin TOUCHARD](mailto:benjamin@kolapsis.com)**