Files
dtf-system/CONTEXT.md
Cauê Faleiros c9f8122600 refactor: give the frontend its own directory and split the API into routers
The Site's page sat at the repository root while its scripts lived in
local/static, a split with no reason behind it. They are together in web/ now,
with the page as index.html, which is also what the image serves.

app.py held the adapters, the configuration, the shared query helpers and
nineteen routes; customer.py held fourteen more but could not import from it
without a cycle, so it was wired by passing nine callables into install_routes.
Configuration and shared helpers move to local/runtime.py, the rules for
attaching artwork to an order move to local/artwork.py where a customer
correction and an operator final-file set can share them, and the routes become
seven routers under local/api. app.py is 48 lines that create the application,
apply the middleware and include them. Routers import downwards only.

Three faults came out of the extraction and are worth recording, because each
passed a check that looked sufficient. ast reports a function's line at the def,
so every decorator on the line above fell outside the extracted range: twelve
routes and the security middleware were defined but never registered, and the
files still imported and parsed cleanly. Names the old closure renamed on the
way in, and a Jsonb import, were missing in three modules. A name-resolution
pass over every new module found those; the route count matching the original
exactly, 32, is what confirmed the first.

The release gate's marker for the fake payment adapter pointed at app.py and the
adapter moved to runtime.py, so the gate passed while the condition it guards was
unchanged. That is the same silent decay 2.5 set out to fix. A test now asserts
every marker still matches something in its file, so the next move fails loudly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 17:22:00 -03:00

404 lines
20 KiB
Markdown

# DTF System - Project Context for AI Agents
## Purpose of this document
Read this document before changing code, documentation, architecture, or scope.
It is the current English source of truth for the project. It supersedes older
decisions in `README.md`, `docs/historico/ESPECIFICACAO.md`, `schema.sql`, and the existing
prototype code whenever they conflict.
Update this file whenever the team makes a material product, process, or
architecture decision.
## Project goal
Build a DTF ordering and production-flow system for Dropstar/Altus.
The current process is slow and manual: customers send artwork through
WhatsApp, staff forward files, designers discover problems late, and production
status is not visible outside the factory. The business has substantially more
machine capacity than it currently uses. The goal is to let customers place DTF
orders online, pay, send artwork, and follow production without making
WhatsApp, shared folders, or manual handoffs the bottleneck.
The customer-facing value is speed and clarity. The operations value is a
traceable queue and fewer lost or manually handled orders.
## Current MVP scope
The MVP must be delivered in **at most three weeks**. The target is a working
online system, not factory-machine automation.
### Included
- Dedicated DTF site/subdomain.
- Existing Site DTF commercial rules and product experience.
- Direct, resumable multipart file upload to Cloudflare R2.
- Mercado Pago payment integration with signed and idempotent webhooks.
- Freight quotation and charging at checkout.
- Idempotent Tiny/Olist order integration and order traceability.
- Online Kanban for production status and secure file download.
- WhatsApp status notifications.
- Private object storage, 30-day file retention, backups, and basic security.
- Operator guidance for the factory team.
### Explicitly outside the three-week delivery
- Automatic pre-flight validation. It will be validated after delivery using
real customer files and real printed output, then refined as support work.
- Direct FlexiPRINT integration.
- Factory-side agent, hot folder, internal file server automation, VPN, and
heartbeat monitoring.
- Production-room dashboard, printer/machine telemetry, and advanced reports.
- Automated shipping-label purchase, shipping-label printing, and dispatch
automation. The MVP freight scope is quote selection and charging only.
- Personalized cart behaviour based on past orders, day, or time.
After delivery, the team provides support and evaluates requested changes. Do
not turn post-delivery support into a new committed delivery phase without an
explicit decision.
## Agreed production model
```text
Customer browser
-> Site DTF / API on VPS
-> direct multipart upload to private Cloudflare R2
-> Mercado Pago payment + freight selected at checkout
-> Tiny/Olist order record
-> online Kanban
-> factory operator downloads final file and imports it manually into FlexiPRINT
```
WhatsApp is a notification channel, not the artwork-upload channel. It is used
for payment confirmation, correction requests, production status, and order
completion.
The operator works through the browser. The factory does not need an installed
agent or an internal server for the MVP.
If the factory internet connection fails, only the factory team temporarily
loses access to the Kanban and secure downloads. The public portal, payments,
uploads, R2 objects, queue, and cloud services remain online. Factory work
resumes when local access returns.
## Infrastructure and deployment
- **VPS:** runs the website, API, worker, PostgreSQL, ClamAV, Kanban, and
third-party integrations.
- **Cloudflare R2:** private S3-compatible object storage for original uploads
and final files. It does not run the application, database, worker, or
antivirus.
- **Upload path:** the browser uploads directly to R2 using short-lived,
server-issued presigned multipart URLs. Large files must never be proxied
through the VPS.
- **Deployment:** one Portainer-owned `dtf-cloud` Docker Swarm stack. One Gitea
Actions workflow tests/scans, publishes `latest` plus commit-SHA application
images, and calls the Portainer webhook. Activation remains blocked pending
the production work listed below.
- **Kanban access:** configure `OPERATOR_EMAIL` and `OPERATOR_PASSWORD` as
stack environment variables. If the email is absent, only Kanban login is
blocked; the rest of the stack remains available.
- **Recommended VPS baseline:** 4 vCPU, 16 GB RAM, and 200 GB NVMe. Existing
VPS capacity may be used if it safely meets or exceeds this baseline.
- **Backups:** PostgreSQL backups go to R2. Application secrets are never
committed to Git.
## File retention
The business does not want an unlimited artwork library.
| Artifact | Retention |
|---|---:|
| Incomplete multipart upload | 1 day |
| Rejected or infected upload | 3 days |
| Customer original after approval | Up to 7 days |
| Final print artwork | Maximum 30 days from the first upload |
| Order history and metrics | Kept in the database without keeping the image |
The 30-day retention decision overrides older references to 90 days or
12-month artwork reordering.
## R2 planning cost reference
R2 cost is driven primarily by the average number of gigabytes stored during a
month; read/write operations are expected to be a much smaller cost at the
project's scale. There is no egress charge in the current planning model.
The client-facing roadmap uses the following planning examples, based on
standard R2 storage pricing, the included storage allowance, and an exchange
rate reference of USD 1 = BRL 5.13:
| Average storage | Approx. R2/month | Approx. BRL/month |
|---|---:|---:|
| 100 GB | USD 1.35 | BRL 7 |
| 500 GB | USD 7.35 | BRL 38 |
| 1 TB | USD 14.85 | BRL 76 |
| 3 TB | USD 44.85 | BRL 230 |
These are planning estimates only. Confirm Cloudflare pricing, exchange rates,
taxes, and payment-provider fees before presenting a commercial quote.
## Commercial rules
The existing rules in `dtf-site.html` are approved as the current source of
truth. Do **not** redesign, simplify, or change prices, discounts, minimums,
rounding, or product modes without explicit approval.
The current site contains four product modes, quality-based price tiers,
artwork-ready discounts, separate assembly charges for loose artwork, a
one-metre minimum, and ten-centimetre billing rounding.
The browser may display the calculation, but production payment creation must
recalculate and validate all price, quantity, freight, and discount values on
the server. Client-provided totals are never authoritative.
Normal JPG/PNG uploads are individual artwork by default, including when uploaded
from the by-metre panel. They use the existing loose-artwork editor, printed
width, quantity, and live packing canvas; packed height determines metres.
Customers uploading an already assembled film-width image must explicitly select
the ready-sheet option before uploading. PDF and manual-format ready sheets keep
their existing workflow. These models retain their separate approved price tiers;
image dimensions alone must never classify a normal artwork as a finished sheet.
## Freight
The original visual freight flow in `dtf-site.html` was a stub with only pickup
functional. The localhost bridge now supports pickup and backend fake freight
quotes; there is still no real carrier quotation or production checkout.
The customer already uses Correios and other shipping platforms. Before freight
can be completed, obtain:
- The platform(s) that should be used as the source of truth.
- API credentials or delegated access for the selected platform.
- Origin postal code/address.
- Available services and carriers to offer.
- Packaging weight and dimensions by DTF length/package.
- Freight business policy: exact pass-through, subsidy, free-shipping rules,
pickup, or other exceptions.
At checkout, the backend must quote freight from the selected source, store the
chosen service and quoted amount, include the amount in the Mercado Pago
payment, and persist it in the order. Do not create a payment before the freight
amount is final.
## Integrations
### Mercado Pago
Required before production payment integration, not for the local mock milestone:
- Production credentials.
- Webhook configuration/access.
Webhook processing must verify authenticity and be idempotent. A duplicate or
late delivery must not create a duplicate payment, order, message, or queue
card.
### Tiny/Olist
Tiny/Olist is already available. The implementation must create/update orders
idempotently and use the order number for traceability. Confirm the actual API
endpoints, marker/tag behaviour, and rate limits before production use.
### WhatsApp
WhatsApp is already available, but the technical provider still needs to be
confirmed (official Meta Cloud API, Z-API, or another provider).
Implement only the required customer events:
1. Payment approved.
2. Artwork correction needed, with a secure link back to the site.
3. Order entered production.
4. Order ready for collection or shipping.
Messages initiated by the business may require approved templates depending on
the provider and conversation state. Use an outbox/job mechanism with retries,
delivery status handling, and idempotency. Do not send artwork through
WhatsApp.
## Factory responsibilities after delivery
The client/factory team is responsible for:
- Opening the Kanban, downloading final files, and importing them manually into
the current FlexiPRINT setup.
- Performing real print tests and reporting mismatches in the final file or
printed result.
- Maintaining PCs, printers, FlexiPRINT licences, printing profiles, internal
folders, and the local factory network.
- Informing the development team about changes to prices, freight, operational
rules, or external platforms that need system changes.
The development team delivers the online system, provides usage guidance, and
supports subsequent corrections or scoped enhancements.
## Three-week roadmap
| Week | Delivery | Result |
|---|---|---|
| 1 | Infrastructure and upload | VPS, domain, database, R2, multipart upload, antivirus, and service foundation. |
| 2 | Payment, freight, and order | Mercado Pago, freight quotation, idempotent Tiny/Olist order creation, final file generation, and main Kanban states. |
| 3 | Kanban and handover | Online Kanban, secure download, WhatsApp status messages, 30-day retention, and operational handover. |
## Known implementation status
### Active localhost milestone (2026-09-15)
The current implementation target is localhost before any production connection.
Use `docker-compose.yml`, `.env.example`, and `LOCAL_SETUP.md`. The runtime is in
`local/`; historical `portal/`, `kanban/`, `agente/`, and `schema.sql` are preserved
as references and are not imported or started by Compose.
- One local `dtf-cloud` Compose project runs seven long-lived services: Site and
Kanban web gateways, FastAPI Portal/API, PostgreSQL, MinIO, ClamAV, and a
PostgreSQL outbox/scanning worker. Separate database and storage initialization
jobs provision restricted runtime identities, for nine Compose services total.
- MinIO implements the S3 storage boundary with direct multipart browser uploads,
resumable parts, private objects, and short-lived signed operator downloads.
- Payment, freight, Tiny/Olist, and WhatsApp use fake adapters only. No production
credentials or integrations are configured. The API and worker have no external
network route; only local web/storage gateways publish loopback ports.
- The original Site commercial calculation and appearance remain the source of
truth. Its existing browser artwork analysis is retained as prototype advice;
no new automatic pre-flight, server artwork analysis, or print-file generation
is implemented or invoked.
- Since client length and grade could be tampered with and automatic pre-flight
is deferred, a local operator manually confirms those commercial inputs before
checkout. This is a development trust-boundary decision, not a new production
promise. The backend calculates all tiers, discounts, assembly-inclusive rates,
one-metre minimum, ten-centimetre rounding, freight, and final totals. Immutable
approved quotes expire after 24 hours. A customer confirms the server total
on the Site to create one idempotent local paid order.
- Paid orders use `rec`, `tra`, `fil`, `imp`, `cor`, `fin`, with allowed transitions,
operator authentication, concurrency checks, and durable movement history.
Local mock integration receipts are visible in the Kanban.
- The customer portal at `/portal.html` supports local registration/login,
stronger scrypt password hashing with legacy-hash upgrade, revocable HttpOnly
sessions, owned order history/status, secure active
file downloads, and correction uploads. Registration/login can claim only the
current guest session's records, never orders found by email or CNPJ. Email
verification and password recovery are not implemented.
- Unfinished carts (including File blobs) recover from IndexedDB for 24 hours in
the same browser, scoped to the guest/account identity. Recovered items can be
removed/replaced or joined by new items; in-place reconstruction of the artwork
editor and cross-device cart synchronization are not implemented. Browser
storage capacity limits apply. Payment clears the saved cart. Logout revokes
the server session and clears local checkout metadata and saved File blobs
across open Site tabs.
- Operators manually upload and approve a complete final-file set, with one or
more files per order item. Queue entry requires active final files for every
item. Corrections invalidate the old set; customers submit corrections through
their portal, and operators reapprove final files. No artwork transformation,
automatic pre-flight, or machine control is performed.
- Every completed upload is quarantined pending a local ClamAV scan. Only `clean`
files can be quoted, commercially approved, paid, downloaded, attached as final
files, or admitted to the print queue. Rejected/error files remain blocked and
expire within three days. The isolated scanner uses signatures bundled in its
pinned image and has no external network route. The transport accepts files up
to 5 GiB, but the local scan/release limit is 128 MiB; larger files remain
blocked. This malware gate is not print pre-flight or artwork validation.
- A retention worker removes expired object bytes but keeps order/file metadata:
incomplete uploads after one day, originals within seven days of manual final
artwork approval, and attached final/correction files within 30 days of the
order's first upload. Storage lifecycle is also a 30-day backstop.
- Structured security events are written to logs and PostgreSQL. The local
`security_status` command summarizes authentication, rate-limit, scan, and
scanner/signature alerts without exposing secrets. Security regression tests,
exact-runtime Python dependency auditing, and Trivy image reports live under
`local/` and `output/security/`; open findings are documented in
`SECURITY_REPORT.md` and are not a production-readiness claim.
- All direct and transitive Python packages are pinned with artifact hashes in
`local/requirements.lock`; the API image build requires those hashes. The lock
is regenerated in a disposable Python 3.12 container by
`local/lock_dependencies.sh`. A fresh exact-runtime audit found no known Python
advisories on 2026-09-15; this does not cover OS/container findings.
- `compose.staging.yaml` is a separate, network-disabled readiness gate only. It
validates non-secret staging decisions and rejects placeholders, local endpoints,
fake providers, embedded secret settings, and unsafe secret sources. It does not
deploy the application, contain credentials, or contact any real provider.
- A separate production delivery package now exists under `deploy/`, with
non-root API/web image definitions, a single Portainer Docker Swarm stack,
external secret/volume contracts, health-monitored rolling updates,
commit-SHA rollback images, and one protected Gitea workflow under
`.gitea/workflows/`. The package never contains provider credentials and has
not been deployed. Its fail-closed preflight intentionally rejects the current
source until production adapters, Docker-secret file loading, approved inputs,
restore rehearsal, image scans, and human security approval are complete.
- `python3 -m local.backup create-and-verify` creates a private, Git-ignored bundle
containing a PostgreSQL dump plus every complete, unexpired object already marked
`clean`. SHA-256 manifests protect both parts. Verification restores the database
under a UUID name and the object bytes under a UUID MinIO prefix, hashes the
restored bytes, then removes only those temporary targets. Active data is never
overwritten; pending, rejected, errored, expired, and purged objects are excluded.
Run this local snapshot while uploads and retention are idle. Scheduling, offsite
copies, and production restore operations remain unfinished.
- Real providers, production authentication hardening, email verification/recovery,
automatic final-file generation, production backup operations, and production
activation remain incomplete. Deployment plumbing exists but cannot pass its
release gate yet. No factory automation or pre-flight is added.
See `docs/historico/IMPLEMENTATION_REPORT.md` for verification, `PRODUCTION_INPUTS.md` for the
decisions and evidence required before staging or production connection, and
`PORTAINER.md` for the production delivery contract.
The client roadmap promises are unchanged, so its PDF source is not regenerated
for this local implementation checkpoint.
### Existing assets
- `web/`: the Site (`index.html`), the Kanban and customer portal pages, and
the scripts behind them. `web/site-*.js` holds the Site's behaviour.
- `docs/historico/`: the original specification, endpoint sketch, task plan and
status report. Background only — they describe a model this system does not
implement.
The prototypes they describe (`portal/`, `kanban/`, `agente/`, the root
`schema.sql` and `.env.exemplo`) were removed on 2026-09-21 once nothing
referenced them; recover from Git history if ever needed.
- `tools/generate_dtf_report.py`: generator for the client-facing roadmap PDF.
- `docs/roadmap-cliente.pdf`: the generated roadmap, as last sent.
- `docs/reuniao-2026-09-09-anotacoes.pdf`:
meeting notes that established the business direction. Treat it as context;
the latest decisions in this document define the active scope.
### Important gaps and stale assumptions
- The original Site had no backend payment, freight quote, order persistence,
authentication, purchase history, or real cart persistence. Local order
persistence, reviewed quotes, mock freight and fake payment now work through
`web/checkout.js`. The local customer portal now provides accounts and
order history; cart recovery is browser-local. Production account verification,
recovery, and cross-device cart editing are still absent.
- Without the local bridge, the original freight fallback remains a stub.
Real freight quotation remains unimplemented in all runtimes.
- The existing `portal/whats.py` and `kanban/whats.py` are duplicated and send
direct text messages. They are not production-ready notification modules.
- Older documentation and code describe a Tiny webhook creating an upload link,
a factory agent, local Kanban, FlexiPRINT automation, 90-day retention, and
12-month artwork reuse. Those are not the current MVP model.
## Documentation synchronization
`context.md` is the compact operating context for agents. The current
client-facing narrative, diagrams, and formatting live in
`docs/roadmap-cliente.pdf`, generated from `tools/generate_dtf_report.py`.
When a decision changes, update both the relevant implementation context here
and the roadmap source when it changes what the client-facing plan promises.
## Change-control rules for agents
- Preserve the current Site DTF commercial rules unless explicitly asked to
change them.
- Do not add factory-side automation, a local agent, hot folders, FlexiPRINT
control, machine dashboards, or a new post-MVP delivery commitment by
inference.
- Do not claim that R2 hosts or executes application services.
- Keep all customer-facing and internal documentation in English only when this
file is the requested artifact; otherwise follow the user's requested
language.
- When a requirement is unclear, distinguish between the MVP, a future
enhancement, and an existing prototype assumption before changing code.