ackify-ce

module
v1.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2025 License: AGPL-3.0, AGPL-3.0-or-later

README ΒΆ

πŸ” 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

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
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/health   # alias: /health
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)"
Optional: Email notifications (SMTP)
ACKIFY_MAIL_HOST="smtp.gmail.com"              # SMTP server
ACKIFY_MAIL_PORT="587"                         # SMTP port (default: 587)
ACKIFY_MAIL_USERNAME="your-email@gmail.com"    # SMTP username
ACKIFY_MAIL_PASSWORD="your-app-password"       # SMTP password
ACKIFY_MAIL_FROM="noreply@company.com"         # Sender address
ACKIFY_MAIL_FROM_NAME="Ackify"                 # Sender name
# If ACKIFY_MAIL_HOST is not set, email service is disabled (no error)

πŸš€ 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
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
    email/              # SMTP service
    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
  • SMTP: Email signature reminders (optional)
  • Docker: Simplified deployment
  • Traefik: HTTPS reverse proxy

πŸ“Š Database

-- Main signatures table
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
);

-- Expected signers table (for tracking)
CREATE TABLE expected_signers (
    id BIGSERIAL PRIMARY KEY,
    doc_id TEXT NOT NULL,
    email TEXT NOT NULL,
    name TEXT NOT NULL DEFAULT '',          -- Display name (optional)
    added_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    added_by TEXT NOT NULL,                 -- Admin who added
    notes TEXT,
    UNIQUE (doc_id, email)                  -- One expectation per email/doc
);

-- Document metadata table
CREATE TABLE documents (
    doc_id TEXT PRIMARY KEY,
    title TEXT NOT NULL DEFAULT '',
    url TEXT NOT NULL DEFAULT '',           -- Document location
    checksum TEXT NOT NULL DEFAULT '',      -- SHA-256/SHA-512/MD5
    checksum_algorithm TEXT NOT NULL DEFAULT 'SHA-256',
    description TEXT NOT NULL DEFAULT '',
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    created_by TEXT NOT NULL DEFAULT ''
);

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
  • βœ… Tracking: Expected signers for completion monitoring
  • βœ… Metadata: Document information with URL, checksum, and description

πŸš€ Production Deployment

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"

# Optional: SMTP for signature reminders
ACKIFY_MAIL_HOST="smtp.company.com"
ACKIFY_MAIL_FROM="noreply@company.com"
ACKIFY_MAIL_USERNAME="${SMTP_USERNAME}"
ACKIFY_MAIL_PASSWORD="${SMTP_PASSWORD}"

πŸ“‹ 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 /health - Health check
Admin
  • GET /admin - Dashboard (restricted)
  • GET /admin/docs/{docID} - Document details with expected signers management
  • POST /admin/docs/{docID}/expected - Add expected signers
  • POST /admin/docs/{docID}/expected/remove - Remove an expected signer
  • POST /admin/docs/{docID}/reminders/send - Send email reminders to pending readers
  • GET /admin/docs/{docID}/reminders/history - Get reminder history as JSON
  • GET /admin/docs/{docID}/metadata - Get document metadata as JSON
  • POST /admin/docs/{docID}/metadata - Create or update document metadata
  • DELETE /admin/docs/{docID}/metadata - Delete document metadata
  • GET /admin/docs/{docID}/status.json - Document status as JSON (AJAX)
  • GET /admin/api/chain-integrity/{docID} - Chain integrity verification 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"
Document Metadata Management

Administrators can manage comprehensive metadata for each document:

  • Store document information: Title, URL/location, checksum, description
  • Integrity verification: Support for SHA-256, SHA-512, and MD5 checksums
  • Easy access: One-click copy for checksums, clickable document URLs
  • Automatic timestamps: Track creation and updates with PostgreSQL triggers
  • Email integration: Document URL automatically included in reminder emails
Expected Signers Feature

Administrators can define and track expected signers for each document:

  • Add expected signers: Paste emails separated by newlines, commas, or semicolons
  • Support names: Use format "Name email@example.com" for personalized emails
  • Track completion: Visual progress bar with completion percentage
  • Monitor status: See who signed (βœ“) vs. who is pending (⏳)
  • Email reminders: Send bulk or selective reminders in user's language
  • Detect unexpected signatures: Identify users who signed but weren't expected
  • Share easily: One-click copy of document signature link
  • Bulk management: Add/remove signers individually or in batch

πŸ” 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
License (AGPLv3)

Distributed under the GNU Affero General Public License v3.0. See LICENSE for details.


Developed with ❀️ by Benjamin TOUCHARD

Directories ΒΆ

Path Synopsis
backend module
cmd
community command
migrate command
internal
application/services
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
domain/models
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
infrastructure/auth
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
infrastructure/config
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
infrastructure/database
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
infrastructure/email
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
infrastructure/i18n
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
presentation/admin
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
presentation/handlers
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
pkg
crypto
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
logger
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
services
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later
web
SPDX-License-Identifier: AGPL-3.0-or-later
SPDX-License-Identifier: AGPL-3.0-or-later

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL