Dabir is an open-source letter numbering and registry system for organizations that need a simple, auditable, and structured replacement for spreadsheet-based letter tracking.
It provides a clean backend API, a web-based admin panel, role-based access control, Jalali date support, Excel migration, manual numbering, scanned letter attachments, S3-compatible object storage integration, and Kubernetes-ready deployment manifests.
- Letter numbering and registry management
- Create, edit, preview, soft-delete, and search letters
- Multiple numbering modes:
- Fixed prefix numbering, such as
DABIR-000001 - Jalali yearly numbering, such as
405-0001 - Manual numbering, such as
405-158,405-ق-103, or any custom structure
- Fixed prefix numbering, such as
- Smart manual number suggestion based on the entered prefix
- Support for Persian and Arabic digits in date and number inputs
- Jalali / Persian calendar support
- Persian and English UI foundation
- RTL-friendly user interface
- Role-based access control
- User management
- Password change and password reset
- Excel import for migrating existing letters
- Import preview and duplicate detection
- Preserving original imported letter numbers
- Audit logs for important system actions
- Setup wizard for first-time initialization
- Optional scanned letter / attachment upload
- S3-compatible object storage support for attachments
- Private attachment storage with presigned download/view URLs
- Attachment management: upload, view/download, and delete
- Dark mode support
- REST API with OpenAPI documentation
- Docker Compose deployment
- Kubernetes deployment with Helm
- External PostgreSQL support
- GitHub Actions CI
Dabir currently supports three access levels:
| Role | Description |
|---|---|
superuser |
Full system access, user management, imports, audit logs, settings, and attachment management |
editor |
Can create, edit, delete, and view letters; can manage attachments |
readonly |
Can view letters and attachments |
- Go
- PostgreSQL
- Chi Router
- JWT authentication
- Clean architecture style
- Database migrations
- S3-compatible object storage client
- Next.js
- TypeScript
- Tailwind CSS
- shadcn/ui-style components
- Jalali date picker
- Dark mode
- Persian / English localization foundation
- Docker Compose
- Helm Chart
- Kubernetes
- External PostgreSQL
- S3-compatible object storage, such as MinIO
.
├── cmd/
│ └── api/
├── internal/
│ ├── application/
│ ├── domain/
│ ├── delivery/
│ ├── infrastructure/
│ └── shared/
├── migrations/
├── web/
├── charts/
│ └── dabir/
├── deployments/
├── docs/
│ ├── openapi.yaml
│ └── wiki/
└── README.md
Create environment file:
cp .env.example .envEdit important values:
DB_PASSWORD=change-this-db-password
JWT_SECRET=change-this-secret-in-production
S3_ENDPOINT=http://localhost:9000
S3_REGION=us-east-1
S3_BUCKET=dabir-attachments
S3_ACCESS_KEY=dabir
S3_SECRET_KEY=dabir_minio_secret
S3_USE_SSL=false
S3_FORCE_PATH_STYLE=true
S3_PRESIGNED_URL_TTL_MINUTES=15
S3_MAX_UPLOAD_SIZE_MB=20Start services:
make compose-upRun migrations:
make migrate-upOpen the web UI:
http://localhost:3000
On the first run, Dabir redirects to the setup page where you can create the first superuser.
Start PostgreSQL and MinIO:
make compose-upRun backend:
export $(grep -v '^#' .env | xargs)
make runRun frontend:
cd web
npm install
npm run devFrontend URL:
http://localhost:3000
Backend URL:
http://localhost:8080
MinIO Console URL:
http://localhost:9001
Default local MinIO credentials, unless changed in .env:
username: dabir
password: dabir_minio_secret
Dabir supports multiple numbering modes to fit different organizational workflows.
Example:
DABIR-000001
DABIR-000002
Example:
405-0001
405-0002
406-0001
Manual numbering stores the letter number exactly as entered by the user.
Examples:
405-158
405-ق-103
1401-MM-DM-95
HR-2026-0042
Manual numbering also includes smart suggestions. When a user starts typing a prefix, Dabir searches for the latest similar number and suggests the next one.
Examples:
Input prefix: 405-
Latest similar number: 405-158
Suggested number: 405-159
Input prefix: 405-ق-
Latest similar number: 405-ق-102
Suggested number: 405-ق-103
Persian and Arabic digits are normalized for suggestion lookup, so inputs such as ۴۰۵-ق- are also supported.
Dabir supports optional scanned letter attachments.
Supported file types:
PDF
JPG / JPEG
PNG
Attachment flow:
Create or edit a letter
Upload one or more optional files
Store files in S3-compatible object storage
Store only metadata in PostgreSQL
View/download files through short-lived presigned URLs
Delete attachments when needed
Attachments are stored privately in object storage. Dabir does not make the bucket public. The backend generates presigned URLs for viewing or downloading files.
Dabir uses S3-compatible object storage for attachments. MinIO is recommended for local development and small to medium on-prem deployments.
Required environment variables:
S3_ENDPOINT=http://minio:9000
S3_REGION=us-east-1
S3_BUCKET=dabir-attachments
S3_ACCESS_KEY=dabir
S3_SECRET_KEY=dabir_minio_secret
S3_USE_SSL=false
S3_FORCE_PATH_STYLE=true
S3_PRESIGNED_URL_TTL_MINUTES=15
S3_MAX_UPLOAD_SIZE_MB=20For production, use a private bucket and a dedicated access key with limited permissions to only the required bucket.
Recommended production notes:
- Keep the bucket private
- Use TLS for the S3 endpoint
- Use a dedicated access key for Dabir
- Configure object storage backup or replication
- Keep S3_MAX_UPLOAD_SIZE_MB aligned with the ingress upload limit
For NGINX Ingress, make sure the upload size is higher than S3_MAX_UPLOAD_SIZE_MB:
nginx.ingress.kubernetes.io/proxy-body-size: "25m"Dabir includes a Helm chart:
charts/dabir
Dabir does not deploy PostgreSQL inside Kubernetes by default. It expects an external PostgreSQL database.
Dabir also expects an S3-compatible object storage endpoint for attachments, such as MinIO, Ceph RGW, or a cloud S3-compatible service.
Create namespace:
kubectl create namespace dabirCreate secret:
kubectl -n dabir create secret generic dabir-secret \
--from-literal=DB_PASSWORD='YOUR_DB_PASSWORD' \
--from-literal=JWT_SECRET='YOUR_LONG_RANDOM_JWT_SECRET' \
--from-literal=S3_ACCESS_KEY='YOUR_S3_ACCESS_KEY' \
--from-literal=S3_SECRET_KEY='YOUR_S3_SECRET_KEY'Install with Helm:
helm upgrade --install dabir charts/dabir \
-n dabir \
--create-namespace \
-f charts/dabir/examples/values-prod.yamlRun database migrations separately against the external PostgreSQL database.
The Helm chart supports S3 configuration through API environment variables and secrets.
Example values:
api:
env:
S3_ENDPOINT: "https://s3.example.com"
S3_REGION: "us-east-1"
S3_BUCKET: "dabir-attachments"
S3_USE_SSL: "true"
S3_FORCE_PATH_STYLE: "true"
S3_PRESIGNED_URL_TTL_MINUTES: "15"
S3_MAX_UPLOAD_SIZE_MB: "20"
secrets:
create: true
existingSecret: ""
DB_PASSWORD: "change-this-db-password"
JWT_SECRET: "change-this-jwt-secret"
S3_ACCESS_KEY: "change-this-s3-access-key"
S3_SECRET_KEY: "change-this-s3-secret-key"When using an existing secret, it must contain:
DB_PASSWORD
JWT_SECRET
S3_ACCESS_KEY
S3_SECRET_KEY
Dabir can import existing letters from Excel files.
Supported format:
.xlsx
Supported logical columns:
| Field | Supported column names |
|---|---|
letter_number |
letter_number, number, no, شماره نامه, شماره |
title |
title, subject, عنوان, عنوان نامه, موضوع |
letter_date |
letter_date, date, تاریخ, تاریخ نامه |
sender |
sender, from, فرستنده, ارسال کننده |
receiver |
receiver, to, گیرنده, دریافت کننده, مقصد |
Import flow:
Upload Excel
Preview and validate
Detect duplicates
Commit import
Preserve original letter numbers
Continue numbering from the imported data
The importer supports real-world legacy files and can preserve original manual letter numbers instead of forcing them into a generated numeric format.
Dabir supports official Iranian Jalali dates in the UI.
Accepted input examples:
1405/03/01
۱۴۰۵/۰۳/۰۱
Dates are stored in PostgreSQL as standard DATE values and returned by the API in both Gregorian and Jalali formats.
Example API response:
{
"letter_date": "2026-05-22",
"letter_date_jalali": "1405/03/01"
}OpenAPI specification:
docs/openapi.yaml
Validate OpenAPI:
docker run --rm -v "$PWD:/work" redocly/cli lint /work/docs/openapi.yamlRun backend tests:
go test $(go list ./... | grep -v '/web/')Run frontend checks:
cd web
npm run lint
npm run buildLint Helm chart:
helm lint charts/dabirRender Helm templates:
helm template dabir charts/dabir -n dabirCheck rendered S3 environment variables:
helm template dabir charts/dabir -n dabir > /tmp/dabir-rendered.yaml
grep -n "S3_" /tmp/dabir-rendered.yamlMore detailed documentation is available in:
docs/
The previous full development guide is archived here:
docs/wiki/full-project-guide.md
- Dashboard statistics endpoint
- Better runtime configuration for web deployment
- Print / export letter confirmation
- Advanced letter search and filters
- Full i18n coverage
- Migration Job support in Helm
- Release automation
- Attachment thumbnails and richer preview experience
- Export letters to Excel / PDF
This project is licensed under the Apache License 2.0.