ParaguayShield Deep Dive Review
Date: 2026-02-11
Repo: /root/clawd/repos/paraguayshield
Files reviewed: 40+
Overall assessment: Well-architected MVP with solid foundations. ~70% complete. Key gaps in security hardening and deployment.
1. Architecture Review
System Overview
ParaguayShield (internally "Pytax") is a set-and-forget tax automation SaaS for Paraguay expats who file zero tax returns. It automates monthly filings to Paraguay's Marangatu tax portal.
Components
| Component |
Tech |
Purpose |
| API |
FastAPI (Python 3.11) |
Core business logic, auth, billing, admin |
| Worker |
Python + APScheduler |
Scheduled filings, compliance checks, retry queue |
| Web |
Static HTML + Alpine.js |
Minimal dashboard, onboarding, admin UI |
| DB |
PostgreSQL 16 |
All persistent state |
| Reverse Proxy |
Caddy 2.8 |
TLS termination, routing |
| Shared Lib |
pytax_shared.marangatu |
Marangatu portal automation client |
Architecture Diagram
User β Caddy (TLS) β Web (static) / API (FastAPI)
β
Worker (APScheduler) β Marangatu Portal
β β
PostgreSQL 16 Mailgun/SMTP
β
Stripe API
Key Design Decisions
- Magic link auth only β no passwords, no OAuth
- Fernet encryption for stored Marangatu credentials (at-rest)
- Stripe for payments (monthly $5 / annual $50), crypto stubbed for future
- Batched filings with jitter to avoid suspicious traffic patterns
- Trial model: first filing free, billing starts after
- Docker Compose deployment on Hetzner, CI/CD via GitHub Actions β GHCR β Ansible
Data Model (8 tables)
users, magic_links, filings, compliance_checks, payments, jobs, admin_audit_log, email_log
Well-normalized. Good use of enums for status fields. UUID primary keys throughout.
2. Security Audit
β
Good
| Area |
Assessment |
| No hardcoded secrets |
All secrets via env vars / Doppler. Default jwt_secret is "dev-jwt-secret" but only used in local/dev |
| Credential encryption |
Fernet (AES-128-CBC) for Marangatu creds, key from env |
| Magic link tokens |
SHA-256 hashed before storage, single-use, 15min TTL |
| HTTP-only cookies |
JWT stored in httponly, secure (in prod), samesite=lax cookie |
| Rate limiting |
Email (3/15min) and IP (10/hr) rate limiting on magic link requests |
| Admin access |
Double-gated: role check + email allowlist check |
| Audit logging |
All admin actions logged to admin_audit_log |
| Stripe webhook verification |
Signature verification when STRIPE_WEBHOOK_SECRET is set |
| Internal API auth |
WorkerβAPI calls use X-Internal-Token header |
| Docker networking |
All services on internal network, only Caddy exposes ports |
β οΈ Concerns
| Issue |
Severity |
Details |
| In-memory rate limiting |
Medium |
_email_rate / _ip_rate dicts reset on restart, don't work across multiple API instances. Use Redis or DB-backed rate limiting. |
| JWT secret default |
Low |
jwt_secret defaults to "dev-jwt-secret". Not a prod risk (env var will override) but could be enforced. |
| No CSRF protection |
Medium |
Cookie-based auth without CSRF tokens. Mitigated by SameSite=lax + CORS, but POST endpoints accepting cookies should have CSRF tokens. |
| Stripe webhook fallback |
High |
If stripe_webhook_secret is empty, webhook payloads are not signature-verified β just parsed as JSON. This allows forged webhook events. |
| Hardcoded AES key in Marangatu client |
Medium |
AES_KEY and AES_IV are hardcoded hex values in client.py. These are for the Marangatu portal protocol (not user data), but should be documented. |
| Hardcoded super user email |
Low |
SUPER_USER_EMAIL = "sgzcz@gmail.com" in admin.py β should be env-configurable. |
| No request body size limits |
Low |
FastAPI defaults are reasonable but explicit limits would be better. |
| Docker runs as root |
Low |
Dockerfiles don't create non-root user. |
| Monero provider enum mismatch |
Low |
Migration 0003 removes monero from enum, but PaymentProvider model still has monero. Code references both btcpay and monero in crypto routes. |
send_text_email overloaded |
Low |
The send_text_email function signature in emailer.py doesn't match call sites that pass template= and user_id= kwargs β these are silently ignored. |
| datetime.utcnow() deprecated |
Low |
Used throughout; should migrate to datetime.now(timezone.utc) (some places already do). |
Payment Security
- Stripe integration is solid: SetupIntent flow, webhook verification, customer portal
- Crypto payments are fully stubbed β auto-settle in 40 seconds (dev only)
- No PCI-sensitive data stored; Stripe.js handles card collection client-side
3. Feature Map
Implemented β
| Feature |
Status |
| Magic link signup/login |
Complete |
| Marangatu credential collection + encryption |
Complete |
| Initial compliance check on onboarding |
Complete |
| Plan selection (monthly/annual, Stripe/crypto) |
Complete |
| Stripe payment setup (SetupIntent flow) |
Complete |
| Stripe subscription creation after trial |
Complete |
| Stripe webhook processing (invoice.paid, subscription.deleted) |
Complete |
| Stripe Customer Portal integration |
Complete |
| Billing cancellation |
Complete |
| Monthly filing automation (Form 211 + 955) |
Complete |
| Weekly compliance checks |
Complete |
| Auto-file overdue (opt-in) |
Complete |
| Catch-up filing for overdue periods |
Complete |
| Retry queue with exponential backoff |
Complete |
| Admin dashboard (users, jobs, payments) |
Complete |
| Admin user CRUD + audit logging |
Complete |
| Admin job retry (single + bulk) |
Complete |
| Admin mark-payment-paid |
Complete |
| Email change flow |
Complete |
| Email notifications (magic links, compliance, filings) |
Complete |
| Admin alerts (CAPTCHA, auth failures) |
Complete |
| Health endpoints (API + Worker) |
Complete |
Stubbed / Partial π§
| Feature |
Status |
| Crypto payments (BTC/Lightning/Monero) |
Stubbed β auto-settles locally, real integration deferred |
| BTCPay/Monero webhooks |
Endpoint exists but returns "deferred" |
Not Started β
| Feature |
Status |
| Production deployment |
Phase 3 not started |
| Observability/monitoring |
Deferred to Phase 2+ |
| TOTP for admin |
Optional, not implemented |
| Email templates (HTML) |
Plain text only |
Value Proposition
"Set it once, forget it." β Expats in Paraguay with zero tax activity pay $5/mo to never think about tax filings again. The system files Form 211 (IVA) and Form 955 (receipt registration) automatically each month, with weekly compliance email updates.
4. Code Quality
Patterns
- Clean separation: API routes β services β DB, Worker has its own storage layer
- Pydantic models for request validation with sensible field constraints
- Dependency injection via FastAPI's
Depends() for auth, DB sessions
- Shared library (
pytax_shared) for Marangatu portal client
- Dataclasses for internal value objects (
BillingError, ComplianceOutcome, etc.)
Test Coverage
11 test files covering:
- Auth (magic links, login, email change)
- Onboarding (credentials, plan selection)
- Billing (Stripe setup, confirm, webhook, cancel)
- Admin (users, jobs, payments)
- Crypto webhooks
- Health endpoints
Test infrastructure is solid: session-scoped DB setup with Alembic migrations, per-test truncation, dependency overrides. Tests run in CI with real PostgreSQL.
Gap: No worker/job tests. No integration tests for the Marangatu client.
Error Handling
- Good defensive error handling in worker jobs (catches all portal errors, classifies retryable vs non-retryable)
- Admin alerts sent on critical failures (CAPTCHA, auth, decrypt)
- Billing errors properly surface to users via email
Issues
- Worker
storage.py uses raw SQL instead of ORM β intentional for performance but creates maintenance burden
- Some code duplication between API compliance service and worker compliance job
emailer.send_text_email doesn't actually accept template/user_id kwargs β these are silently dropped (Python **kwargs not used)
5. Business Analysis
Target Market
- Primary: Foreign expats living in Paraguay (estimated thousands) who have zero local tax activity but must file monthly returns
- Geography: Paraguay-specific (Marangatu portal)
- Pain point: Missing filing deadlines β fines, compliance issues, risk to residency
Pricing
| Plan |
Price |
Payment |
| Monthly |
$5 USD |
Stripe |
| Annual |
$50 USD |
Stripe or crypto |
Revenue Model
- SaaS subscription with free trial (first filing)
- Very low operational cost (single Hetzner VPS)
- Potential: 100 users Γ $5/mo = $500/mo or $6K/yr
Competitive Advantage
- Automation: No other service automates Marangatu filings for zero-filers
- Simplicity: No app to install, no passwords to remember
- Crypto-friendly: Annual payments via BTC/Lightning/Monero (appeals to target demographic β many Paraguay expats are crypto-oriented)
Risks
- Portal changes: Marangatu UI changes could break automation
- CAPTCHA escalation: Portal could add CAPTCHAs more aggressively
- Legal: Automating government portal filings may have regulatory implications
- Small TAM: Niche market limits scale
6. Planning Review
Project State: ~70% Complete
| Phase |
Status |
Description |
| Phase 1: Stripe Testing |
β
Complete (2026-01-19) |
4 plans, all done |
| Phase 2: Crypto Stubs |
β
Complete (2026-01-21) |
3 plans, all done |
| Phase 3: Deployment |
β Not started |
Deploy to staging + production |
Velocity
- 7 plans completed in ~0.4 hours total
- Average 3 minutes per plan
- Very fast execution, well-structured planning
What's Next
- Phase 3: Deployment β Deploy to staging (paraguayshield.xyz) and production (paraguayshield.io) with Stripe enabled
- External crypto payment server integration (future milestone)
- Observability (future)
Deferred Items
- OpenAPI spec for crypto payment server API contract
- External BTCPay/Monero integration (stubs in place)
- TOTP admin auth
- Monitoring/alerting
7. Recommendations
P0 β Must Fix Before Production
| # |
Issue |
Action |
| 1 |
Stripe webhook without signature verification |
Enforce STRIPE_WEBHOOK_SECRET β reject webhooks if not configured. Currently falls through to unsigned parsing. |
| 2 |
Email function signature mismatch |
send_text_email() is called with template= and user_id= kwargs that it doesn't accept. Fix the function signature or add EmailLog recording. |
| 3 |
PaymentProvider enum mismatch |
Model has monero enum value but migration 0003 removes it from DB. Remove monero from the Python enum or add agentmaker (referenced in admin.py mark_payment_paid). PaymentProvider.agentmaker is referenced but doesn't exist in the enum. |
| 4 |
Non-root Docker user |
Add RUN useradd -r app && USER app to Dockerfiles. |
P1 β Should Fix Soon
| # |
Issue |
Action |
| 5 |
In-memory rate limiting |
Replace dict-based rate limiting with DB or Redis-backed solution. Current implementation resets on restart and doesn't work across instances. |
| 6 |
CSRF protection |
Add CSRF tokens for state-changing endpoints using cookie auth. |
| 7 |
Hardcoded super user email |
Move SUPER_USER_EMAIL to env config. |
| 8 |
Worker test coverage |
Add tests for jobs.py β the most critical business logic (monthly filing, retry queue, catch-up). |
| 9 |
datetime.utcnow() migration |
Replace deprecated datetime.utcnow() with datetime.now(timezone.utc) throughout. |
| 10 |
Enforce required env vars in production |
Add startup validation that critical vars (JWT_SECRET, CREDENTIALS_KEY, STRIPE_WEBHOOK_SECRET) are set and non-default when APP_ENV=production. |
P2 β Nice to Have
| # |
Issue |
Action |
| 11 |
HTML email templates |
Replace plain text emails with branded HTML templates. |
| 12 |
Structured logging |
Add JSON structured logging for production observability. |
| 13 |
Marangatu client tests |
Add integration tests with recorded responses (VCR/responses library). |
| 14 |
DB connection pooling config |
Explicitly configure SQLAlchemy pool settings for production. |
| 15 |
Dependency updates |
python-jose is abandoned; migrate to PyJWT or joserfc. FastAPI 0.110 is old β update to latest. |
| 16 |
API versioning |
Consider /api/v1/ prefix for future compatibility. |
| 17 |
Compliance code DRY |
Deduplicate compliance check logic between services/compliance.py and worker/jobs.py. |
| 18 |
Health check auth |
Worker health endpoint on port 8001 is unauthenticated (acceptable for internal use, but document). |
Summary
ParaguayShield is a well-designed, focused MVP solving a real pain point for Paraguay expats. The codebase is clean, well-structured, and demonstrates good engineering practices (magic link auth, encrypted credentials, retry queues, admin audit logging). The main gaps are: (1) a few security hardening items before production deployment (especially Stripe webhook verification), (2) missing worker tests for the most critical business logic, and (3) completing Phase 3 deployment. The project is ~70% to launch-ready with the remaining work being deployment configuration and the P0 fixes above.