https://github.com/anandukch/aegis
PII privacy vault API in Go - tokenize sensitive data, role-based access, encrypted storage, audit trail.
https://github.com/anandukch/aegis
aes-gcm audit-logging data-privacy encryption golang pii rbac security tokenization
Last synced: about 1 month ago
JSON representation
PII privacy vault API in Go - tokenize sensitive data, role-based access, encrypted storage, audit trail.
- Host: GitHub
- URL: https://github.com/anandukch/aegis
- Owner: anandukch
- Created: 2026-06-02T20:29:15.000Z (2 months ago)
- Default Branch: master
- Last Pushed: 2026-06-16T06:26:49.000Z (about 2 months ago)
- Last Synced: 2026-06-16T07:46:33.457Z (about 2 months ago)
- Topics: aes-gcm, audit-logging, data-privacy, encryption, golang, pii, rbac, security, tokenization
- Language: Go
- Homepage: https://anandukch.github.io/aegis/
- Size: 3.78 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
A **PII Data Privacy Vault** REST API built in Go. Instead of storing raw sensitive data (emails, phone numbers, card numbers) in your application database, you send them to AEGIS, get back an opaque token, and store that token. Only authorized roles can retrieve the real data — and every access is logged.
---
## Architecture
---
## Quickstart
```bash
cp .env.example .env
# Edit .env: set JWT_SECRET (32+ chars) and VAULT_MASTER_KEY (exactly 32 chars)
docker-compose up --build
```
That's it. Postgres starts, migrations run, API is live at `http://localhost:8080`.
---
## Usage
Full step-by-step guide with curl examples for every endpoint → **[docs/USAGE_GUIDE.md](docs/USAGE_GUIDE.md)**
API reference → **[docs/API.md](docs/API.md)**
Swagger UI (GitHub Pages) → **https://anandukch.github.io/aegis/swagger.html**
---
## RBAC Access Matrix
| Role | email | phone | card_number | aadhaar | pan | name | dob |
|----------|--------|--------|-------------|---------|--------|--------|--------|
| ADMIN | FULL | FULL | FULL | FULL | FULL | FULL | FULL |
| ANALYST | MASKED | MASKED | **DENIED** | MASKED | MASKED | MASKED | MASKED |
| SERVICE | MASKED | MASKED | FULL | MASKED | MASKED | MASKED | MASKED |
| VIEWER | MASKED | MASKED | MASKED | MASKED | MASKED | MASKED | MASKED |
**Masking examples:**
- email: `john@example.com` → `j***@example.com`
- phone: `9876543210` → `******3210`
- card: `4111111111114242` → `****-****-****-4242`
- aadhaar: `123456781234` → `XXXX-XXXX-1234`
- pan: `ABCDE1234F` → `ABCDE****F`
- name: `John Doe` → `J*** D***`
- dob: `1990-07-15` → `****-**-15`
---
## Threat Model — What Happens If AEGIS Is Compromised?
This is the right question to ask about any vault system.
### Database leaked (without the master key)
Attacker gets rows of `enc_value` + `nonce` — both base64-encoded ciphertext. Without `VAULT_MASTER_KEY`, these are computationally infeasible to reverse. AES-256-GCM with a random nonce per record gives no pattern to exploit. **Raw PII remains safe.**
### `VAULT_MASTER_KEY` leaked
This is the single critical secret. If it leaks alongside the database, every record can be decrypted. **This is game over for all stored data.**
Mitigation for production:
- Never store the master key in a `.env` file on the server
- Use a dedicated secrets manager: **AWS KMS**, **HashiCorp Vault**, or **GCP Secret Manager**
- Ideally use envelope encryption — the master key itself is encrypted by a hardware-backed KMS key that never leaves the HSM
- Rotate the master key periodically (requires re-encrypting all vault records)
### `JWT_SECRET` leaked
Attacker can forge valid JWTs with any role (including `ADMIN`), then call `/detokenize` to retrieve raw values — provided the API is reachable and the DB is accessible. **Effectively full read access.**
Mitigation: short `JWT_EXPIRY_HOURS`, rotate the secret immediately on suspicion, use RS256 (asymmetric) JWTs in production so the signing key never needs to be shared.
### Audit logs as a detection layer
Every detokenize call is logged with `actor_id`, `ip_address`, and `timestamp` — even denied ones. Anomalous patterns (high volume from one actor, unusual IPs, DENIED spikes) signal an active breach before full exfiltration completes.
### Summary
| What leaks | Impact | Mitigation |
|---|---|---|
| Database only | None — ciphertext is opaque | AES-256-GCM (built in) |
| `VAULT_MASTER_KEY` | Full decryption of all records | Store in KMS, never in env files |
| `JWT_SECRET` | Attacker can impersonate any role | Short expiry, rotate immediately, use RS256 |
| Both key + DB | Total breach | Defense in depth — restrict DB network access, monitor audit logs. Production-grade mitigation: use Customer-Managed Keys (CMK) where the KEK lives in the customer's own AWS KMS account — even a full vendor-side compromise cannot decrypt data without the customer's key. |
---
## Design Decisions
**AES-256-GCM** — authenticated encryption. GCM provides both confidentiality and integrity: any tamper with the ciphertext causes decryption to fail rather than silently return garbage. A fresh 12-byte random nonce per record ensures identical values produce different ciphertexts.
**Soft deletes** — `deleted_at` timestamp instead of hard delete. Audit logs reference tokens; hard-deleting vault records would leave dangling audit entries. Soft delete preserves the audit trail while making the data inaccessible through normal API flows.
**Append-only audit logs** — no update or delete endpoint for audit records. The audit trail's value is in its immutability. Every STORE, DETOKENIZE, and DELETE is logged regardless of outcome.
**Role embedded in JWT** — no DB lookup per request for role resolution. Role changes only take effect on next login (short JWT expiry mitigates stale role window).
---
## Running Tests
```bash
go test ./internal/crypto/... ./internal/vault/... -v
```
---
## What's Next
See [`internal/llmproxy/README.md`](internal/llmproxy/README.md) — planned LLM proxy that detokenizes PII before sending prompts to external models, preventing raw PII from ever reaching OpenAI/Anthropic.
---
## Environment Variables
| Variable | Description |
|----------|-------------|
| `APP_PORT` | HTTP listen port (default: 8080) |
| `APP_ENV` | `development` or `production` |
| `DB_HOST` | Postgres host |
| `DB_PORT` | Postgres port |
| `DB_USER` | Postgres user |
| `DB_PASSWORD` | Postgres password |
| `DB_NAME` | Database name |
| `JWT_SECRET` | HMAC secret for JWT signing (min 32 chars) |
| `JWT_EXPIRY_HOURS` | JWT TTL in hours (default: 24) |
| `VAULT_MASTER_KEY` | AES-256 master key — **must be exactly 32 bytes** |