Atta Star web
Business control for my company: invoices read from their QR codes and matched to the bank, the timesheet, and the projects at a glance. The accountant has a read-only view of the books; a project manager at a client checks the hours billed to them and suggests changes, which come to me to approve.
- Role
- Sole developer: design, build and hosting
- Year
- 2026
- Outcome
- One place to see what each project is waiting on, how the week's hours went, and which invoices and bank movements still need matching, without opening five tools.
- Built with
- Node 22, TypeScript, Vue 3, Pinia, SCSS, Vite, Vitest, Playwright, pnpm workspace

Why I built it
Working alone across several projects, the state of each one was spread around: git, to-do lists, notes and Claude Code session logs. Hours had to be rebuilt by hand at the end of the month, and the company's invoices and bank movements were matched in a spreadsheet.
What it does
To do
The Business page opens on one list of what is waiting on me: an e-Fatura import more than 35 days old, a line in e-Fatura with no document behind it, a photo whose code could not be read, an online order with no invoice, a request from the accountant, a statement for an account it does not know. The sidebar badge counts them, and anything before the date the books were closed drops off.

Invoices read from their QR code
Every invoice and receipt the company receives is filed by its Portuguese fiscal QR code, whether it came from the invoices mailbox, an upload or the phone's camera, which reads the code and then takes a photo to crop. The reader copes with codes drawn as images, as vector shapes or in a barcode font, with a second decoder as a fallback. Each document says what it is (a cost, a sale, not an invoice), can be marked as paid from my own pocket, and goes on to the accounting software.

e-Fatura
The tax authority's list of the month's invoices, received and issued, sits beside the documents. Each line is matched to its document or marked Missing, and can be linked by hand or waived.

Bank
Accounts are read through PSD2 open banking, read-only, with consent given at the bank itself: once a day in the background, or on demand while I am there. Matching applies only the certain cases (bank fees, transfers between my own accounts, an exact amount within ten days of an invoice) and proposes the likely ones for one click. Statement PDFs fill in older history, and one whose running balances do not add up is refused whole. A separate section keeps the running balance between me and the company.

Overview
The year in accounting terms beside the cash in the bank: who owes what, months and quarters with VAT, a corporate-tax estimate, what is coming up, and years side by side. A year report prints in English or Portuguese.
Timesheet
Nobody types hours in. A small publisher on each of my computers reads the coding sessions and git state and sends working blocks across. The timesheet shows them by day, week or month, as a ledger, a calendar, per client or as an invoice. A block can be retimed, split, moved to another project, excluded or marked billable, and each client has a "billed through" date. It exports to CSV, Excel and PDF.

The accountant and clients
The accountant gets a read-only view in Portuguese (documents, e-Fatura, bank and overview) and downloads a period as a zip, a spreadsheet or a monthly pack. A missing document can be asked for, and the request lands in my To do. A client sees only their own billable hours, without clock times or amounts, and can suggest a change or comment on a line. Suggestions wait in Approvals until I accept or reject them.

Projects and wikis
One card per project with its branch, uncommitted changes, last commit and open tasks, and links to the editor, the live site and its wiki. The shared knowledge base and every project's wiki can be read here, with an outline and search.

Notifications
Phone notifications only for what needs acting on: an invoice that turns 30 days unpaid (once, and not before nine in the morning), a lockout after repeated failed sign-ins, an off-site backup that has not arrived in 36 hours, and each connected app's daily nudge.

How it works
- Sign-in and roles
- A single sign-on page with two-factor sign-in stands in front of the app, which has no port of its own. One access module decides every route and refuses anything it does not allow. There are four roles: owner, collaborator, client and accountant. Anyone but the owner is refused a write before the request is even read, and every change must carry a custom header against cross-site requests. I can view the site as another user, read-only and audited. The audit log keeps every export, decision, invoice change and accountant read for 90 days.
- Secrets
- Keys come from a fixed, checked list, live in owner-only files and never reach a page. A page only ever sees the last four digits of an IBAN. Bank calls are signed with short-lived tokens. The tax portal password is never held: an Atta skill fetches the month's e-Fatura file in a browser where I sign in myself.
- The publishers
- Each computer gets its own token, shown once. Only its hash is stored; it is compared in constant time, can be revoked, and opens the routes that receive data and nothing else. Session transcripts never leave the computer, only working blocks with short snippets of each prompt. An edit made while the server cannot be reached waits in a local outbox.
- The household finance app
- The household finance app reads two figures from Atta Star through one read-only bridge, server to server: business invoices I paid with a personal card, and the running balance the company owes me. Its token opens that one path, the reply is capped in size, and every read is audited. Atta Star asks nothing back. Other apps can show a small status card on the Projects page, three lines at most, without Atta Star knowing what the numbers mean.
- Storage
- There is no database: data is JSON files, written to a temporary file and renamed into place. A file that cannot be read stops the route rather than being saved over. Open pages get changes over one event stream and reload only the part that changed.
How it is built and tested
- Tests
- About 2,500 unit tests and six Playwright suites: accessibility checks with axe, phone-width runs, and the business, approvals, client and documents flows. Each test was proved by breaking the code on purpose and watching it fail. Fake invoice PDFs with valid QR codes, fake statements, a fake bank and a fake Google stand in for the real ones.
- Releases
- Every pull request runs type checks, lint, tests, the build and packing. A merge publishes a release archive with its checksum. Nothing on GitHub can reach the server: the server pulls the release, checks the hash and the contents, and tests the new proxy configuration before reloading. The server is bundled into one file, with no node_modules.
How Atta helped
- Every pull request went past atta-critic, an independent reviewer with no memory of the session that wrote the code. Its first real use caught two false claims in a PR description.
- Open decisions were settled before code with /atta-grill, one question at a time, and the reasoning kept in the project wiki.
- What it learned about banks, statements and invoices was written up in the shared wiki, so the household finance app started from it instead of from scratch.
- The repo rule is no real figures: tests use made-up amounts and names, and a pre-commit scan refuses anything shaped like a tax number or IBAN.