Skip to content

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

  1. Phase 3: Deployment β€” Deploy to staging (paraguayshield.xyz) and production (paraguayshield.io) with Stripe enabled
  2. External crypto payment server integration (future milestone)
  3. 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.