Infra-009: Finance Report Deployment¶
Status: In Progress
Owner: Infra
Priority: P1
Created: 2026-01-10
Goal¶
Deploy Finance Report application (FastAPI + Next.js) with independent PostgreSQL and Redis instances.
Context¶
Finance Report is a personal financial management system that requires: - PostgreSQL 16 for double-entry bookkeeping data - Redis for caching/sessions - MinIO for statement file storage (shared with platform)
The deployment uses vault-init pattern for secrets management.
Scope¶
This project is managed in the finance_report repository. See the main EPIC document:
Architecture¶
finance_report/finance_report/
├── 01.postgres/ # Dedicated PostgreSQL instance
├── 02.redis/ # Dedicated Redis instance
└── 10.app/ # Backend + Frontend application
Dependencies¶
vault → postgres → app
redis ──────┘
minio (platform/03.minio)
Domain¶
- Production:
report.${INTERNAL_DOMAIN} - Single domain for FE + BE (API at
/api/*)
Deliverables¶
- finance_report/finance_report/01.postgres/
- finance_report/finance_report/02.redis/
- finance_report/finance_report/10.app/
Vault Secrets Setup¶
Before deploying the application, configure the following secrets in Vault:
App Secrets (secret/data/finance_report/production/app)¶
```bash
1. Database connection string¶
invoke env.set DATABASE_URL=postgresql+asyncpg://postgres:
2. Redis connection string¶
invoke env.set REDIS_URL=redis://:
3. MinIO/S3 configuration (internal endpoint)¶
invoke env.set S3_ENDPOINT=http://platform-minio:9000 \ --project=finance_report --env=production --service=app
4. MinIO/S3 public endpoint (REQUIRED for OpenRouter AI access)¶
invoke env.set S3_PUBLIC_ENDPOINT=https://s3.zitian.party \ --project=finance_report --env=production --service=app
5. MinIO access credentials¶
invoke env.set S3_ACCESS_KEY=finance-report-app \ --project=finance_report --env=production --service=app
invoke env.set S3_SECRET_KEY=
6. MinIO bucket name¶
invoke env.set S3_BUCKET=finance-report-statements \ --project=finance_report --env=production --service=app
7. OpenRouter API key (for AI document parsing)¶
invoke env.set OPENROUTER_API_KEY=
MinIO Security Configuration¶
MinIO bucket and user setup is automated via the invoke fr-app.setup command. The deployment script calls the shared create_app_bucket task from platform/03.minio/shared_tasks.py.
What happens automatically:
1. Creates bucket finance-report-statements
2. Generates MinIO service account credentials (if not already in Vault)
3. Configures bucket with security best practices:
- Public download access (required for OpenRouter to access files via direct public URLs)
- Server-side encryption (SSE-S3)
- Lifecycle policy: auto-delete files after 90 days
4. Stores credentials in Vault (secret/data/finance_report/production/app)
To manually configure or recreate bucket:
```bash
Using the shared MinIO task¶
invoke minio.create-app-bucket \ --bucket-name=finance-report-statements \ --enable-encryption=True \ --lifecycle-days=90 \ --public-download=True ```
Security Impact: - ✅ Public can download files anonymously when the object URL is known (public-read bucket) - ❌ Public cannot list bucket contents - ❌ Public cannot upload or delete files - ✅ Only application service account can upload/delete - ✅ Data encrypted at rest (SSE-S3) - ✅ Auto-deletion after 90 days reduces exposure window
Optional Manual Hardening:
```bash
Enable versioning (protect against accidental deletion)¶
docker exec platform-minio mc version enable local/finance-report-statements
Enable audit logging (track file access)¶
Via MinIO Console: https://minio.zitian.party¶
Settings → Audit → Enable¶
```
Verification:
```bash
Check bucket configuration¶
docker exec platform-minio mc ls local/finance-report-statements docker exec platform-minio mc anonymous get local/finance-report-statements # Should show "download" docker exec platform-minio mc encrypt info local/finance-report-statements # Should show "sse-s3" docker exec platform-minio mc ilm ls local/finance-report-statements # Should show 90-day expiry ```
Critical: S3_PUBLIC_ENDPOINT¶
The S3_PUBLIC_ENDPOINT is required for production deployments because:
- OpenRouter AI service needs to download statement PDFs for parsing
- Internal endpoints (e.g., http://platform-minio:9000) are not accessible from OpenRouter
- Without this, system falls back to Base64 encoding which is limited to files <5MB
- Production value: https://s3.zitian.party (publicly accessible MinIO S3 API endpoint)
Observability (SigNoz Logs)¶
- OTEL export is optional in app code; enabled via Vault in staging/production.
- OTEL endpoint should be environment-specific (no placeholders in Vault).
- Required keys in Vault:
OTEL_EXPORTER_OTLP_ENDPOINT,OTEL_SERVICE_NAME,OTEL_RESOURCE_ATTRIBUTES.
PR Links¶
- infra2: https://github.com/wangzitian0/infra2/pull/67
- infra2: https://github.com/wangzitian0/infra2/pull/116 (S3 endpoint fix)
Change Log¶
| Date | Change |
|---|---|
| 2026-01-10 | Initialized project, linked to EPIC-007 |
| 2026-01-19 | Added SigNoz log export wiring for finance_report app |
| 2026-01-19 | Set OTEL Vault values for finance_report app (staging/production) |
| 2026-01-19 | Added restart-safe compose wiring and fixed Vault template quoting |
| 2026-02-02 | Staging Investigation: Found missing S3_PUBLIC_* in Vault, added them (v36) |
| 2026-02-02 | Root Cause Found: get_service_url() was adding env suffix to shared services |
| 2026-02-02 | Fix Applied: Updated libs/common.py to skip suffix for SHARED_PLATFORM_SERVICES |
| 2026-02-02 | PR #116 created: Skip environment suffix for MinIO/Vault/Dokploy/SSO public URLs |
Verification¶
- [ ]
invoke finance_report.postgres.status - [ ]
invoke finance_report.redis.status - [ ]
invoke finance_report.app.status - [ ]
curl https://report.${INTERNAL_DOMAIN} - [ ] Logs visible in SigNoz (after OTEL vars are set)