2025-09-20 12:27:20 +02:00
2025-09-20 12:27:20 +02:00
2025-09-19 23:46:25 +02:00
2025-09-19 23:46:25 +02:00

🔐 Ackify

Proof of Read. Compliance made simple.

Secure document reading validation service with cryptographic traceability and irrefutable proof.

Build Security Go License

🇫🇷 Version française disponible ici

🎯 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
Initialisation d’une signature
2) User sign flow
Parcours de signature utilisateur

📸 Screenshots

Home page
Home page
Signing request
Signing request
Signature confirmed
Signature confirmed
Signatures list
Signatures list
Outline integration
Outline integration
Google Docs integration
Google Docs integration

Quick Start

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
Google 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_at protected 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 login
  • GET /logout - Logout
  • GET /oauth2/callback - OAuth2 callback

Signatures

  • GET /sign?doc=<id> - Signature interface
  • POST /sign - Create signature
  • GET /signatures - My signatures (auth required)

Consultation

  • GET /status?doc=<id> - JSON all signatures
  • GET /status.png?doc=<id>&user=<email> - PNG badge

Integration

  • GET /oembed?url=<embed_url> - oEmbed metadata
  • GET /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

SSPL License

Free usage for internal projects. Restriction for competing commercial services. See LICENSE for complete details.


Developed with ❤️ by Benjamin TOUCHARD

Description
Proof of Read. Compliance made simple.
Readme AGPL-3.0 21 MiB
Languages
Go 67.6%
Vue 14.3%
TypeScript 10.8%
Shell 2.4%
PLpgSQL 2%
Other 2.9%