· DYNOKORE TEAM

We're building an invoicing system with AI agents, in public

DynoInvoice is an e-invoicing and GST compliance platform for Singapore, and AI coding agents are writing it. Its source is now on GitHub at github.com/dynokore/DynoInvoice. The mirror syncs every hour, so you can watch the agents' commits land while they work. Development is ongoing, and incomplete features are expected.

The source is published for study only. It's licensed under PolyForm Strict 1.0.0, which allows reading and running it for non-commercial purposes, and nothing else. We don't accept contributions. It isn't open source in the OSI sense, and we don't call it that.

What DynoInvoice is

Singapore is moving GST invoicing onto InvoiceNow, the national e-invoicing network built on Peppol. GST-registered businesses will have to send their invoice data to IRAS through it, using an InvoiceNow-Ready solution and an accredited Access Point.

DynoInvoice sits beside the systems a business already runs (ERP, billing, point of sale, expenses) rather than replacing them. It handles what the InvoiceNow requirement asks of a GST-registered business:

  • deciding which transactions are in scope, and mapping internal tax codes to IRAS GST categories;
  • building and validating the invoice data IRAS requires, and working out when each submission is due;
  • submitting it to IRAS through an accredited Access Point, and handling resubmissions and credit notes;
  • keeping the evidence, and reconciling what was submitted against what was filed in the GST return.

Accounting firms and tax agents can act for their clients, with each client's consent for each entity. It runs as a multi-tenant service or as a single-tenant private deployment, from the same code. It doesn't run its own Access Point, prepare GST returns, or replace accounts receivable and payable.

The foundations are in place: organisation structure, access control (including acting on a client's behalf) and the shared contracts between services. Document handling, submission and reconciliation are still being built.

Why we're building it

The requirement already applies to new voluntary GST registrations, and IRAS has set dates to bring in most other GST-registered businesses in phases through April 2031, so most businesses will need something between their existing systems and the network.

We're building DynoInvoice for two reasons. It's a product we intend to bring to market when it's ready. And it's the hardest test we could find for DynoKore Keel. Invoicing has tenants that must never see each other's data, rules that come from regulation rather than taste, and an audit trail someone will eventually ask for. If agents can build that under governance, the method should hold for less demanding software.

How it's designed

DynoInvoice is designed the way a regulated, multi-tenant system has to be. The diagram shows the design; dashed parts aren't built yet.

DynoInvoice architecture: customer systems connect through an API gateway to a pipeline of services (ingestion, documents, payload and validation, submission, transport) that sends to an accredited Access Point and on to IRAS; aggregation, rules and master data sit beside it; evidence, reconciliation, notification and the operator console consume events; tenancy, access, the event log and shared libraries form the foundation; an operator-only control plane publishes signed rule packs. Dashed parts are designed but not built yet.
DynoInvoice architecture. Solid boxes have code on main and are still in progress; dashed boxes are designed but not built yet.
  • One domain per service, one schema per service. Each service owns its own database schema and can't read another's tables. Services talk through contracts published from one place and checked automatically: a few synchronous APIs where the caller needs an answer, and events everywhere else, delivered at least once to consumers that must handle repeats.
  • Tenant isolation in the database. Every table carries the tenant, and isolation is enforced by the database rather than by application filters. A private deployment runs the same multi-tenant code with a single tenant.
  • Submissions keep flowing. Tokens are checked offline at the gateway, and evidence, reconciliation and notifications consume events without holding up the pipeline.
  • Evidence you can verify. Receipts, decisions and audit logs go to an append-only store kept apart from operational data, with integrity checks.
  • Rules as signed data. Tax rules are versioned data in signed rule packs, so the same date and input give the same result. The control plane that signs them runs only at the operator, never receives tenant data, and isn't needed at run time: a private deployment can run fully offline, without new rule packs until it reconnects or loads one.

How the agents are governed

The agents work under DynoKore Keel, which we built for ourselves first.

One chain, end to end. Requirements, acceptance criteria and tasks are versioned records in Keel, and every change traces back to them. Before a coding agent starts, the server checks the task's requirement and design readiness and lists each missing item; a task that fails stays blocked.

The author doesn't grade its own work. By rule, the agent that drafts a requirement or decision change submits it, and a separate examiner agent approves and publishes it. Every merge request must pass the repository's automated gates and an independent reviewer agent's check against the project's written engineering standards, including any change to tests (documentation-only changes run text checks only).

Done is decided by the server, not the coder. Tests must prove acceptance criteria held in Keel. The coding agent can't change those criteria, and a rule forbids weakening a check to get green. A coding task is marked done by the server from the merged change and its CI verdict, not by an agent, and a change to a task's acceptance criteria sends it back for review.

People answer the questions that matter. Questions about functionality, direction, exemptions or relaxing a check are answered by a person, and the answer is recorded in Keel with who decided. Loosening a check is never an agent's call. When an agent reaches one of these questions, that task waits and the agents carry on with other work; once the person answers, it picks up again. Most other approvals are made by agents under a delegation from that person, and the record shows which is which.

Credentials stay out of the agents' reach. A hook blocks agents from reading credential files; tools read credentials in their own process and never print them. Before every sync, the published history is scanned for credentials and internal identifiers, and any finding stops the sync.

What went wrong

Agents fail in ways that look like success. A few things we caught, and what we changed:

  • Green, but not done. A coding agent reported a task complete with a green pipeline. It had pushed only scripts and fixtures; the main change was never committed. Coding agents are now required to have a clean working tree, with a diff covering every file the task names, before reporting done, and independent review checks the diff against the task.
  • A looser reading to get through. An agent decided on its own that part of a requirement didn't apply, which made the task easy to pass. It was sent back. A coding agent that hits a gap is instructed to stop and report it; it may not pick a looser reading of the task.
  • A test that passed on review day. A test hard-coded a moment in time while the SQL used the database clock. It was green when reviewed, then turned main red a couple of days later and blocked every merge request. Review now looks for time literals, and time-sensitive tests run with both the application and database clocks shifted.
  • A scan that saw nothing. A licence scan with no findings was cited as evidence, but it had scanned the source tree and never saw the built artifacts. Before a green scan counts as evidence, we check that it actually saw what it claims to cover.
  • A quotation nobody wrote. An agent drafting a decision included a quotation that didn't exist in its source. The independent examiner caught it before publication. Quotations must now be exact and cite the version they come from.
  • Agents waiting on each other. One agent was allowed to dispatch another. When every slot was full, each waited on a slot the other held. Agents no longer dispatch other agents; the coordinator does, and a script caps how many run at once.

What's next

We're building a public view of the record, where you'll be able to pick a commit and follow it back to its task, requirement and review.

DynoInvoice is still being built. If you want to ask how the agents built something, the repository's Discussions are open.

We're also opening Keel to a small group of design partners: engineering teams already using coding agents who need to answer to an auditor, a client or a regulator. Apply at dynokore.ai.

If you find something to criticise in the code, that's the point. Tell us.