Documentation
Architecture notes presented like a product surface, not a raw Markdown dump.
This reference is sourced from the public design document and framed for founders, designers, and engineers who need the rationale behind INFOC ONE.
INFOC ONE — Design Thinking & Architecture
design.infoc.one | Public | April 2026
What this is: How we think about building INFOC One. The principles, frameworks, and decisions that shape every product and engineering choice we make. Written for developers, designers, and technical founders who want to understand the platform deeply.
What this is not: A marketing document. A competitor analysis. A feature list. Everything here is about how and why, not what and how much.
Table of Contents
- The Problem We Are Solving
- Design Principles
- Platform Architecture
- The ONE Naming System
- ONEACCESS — Access Control Philosophy
- ONEUI — Design System
- ONE Build — Extensibility
- ONE AI — Intelligence Architecture
- ONE Ship — Deployment Philosophy
- Singapore First
- What We Will Never Do
- Changelog
1. The Problem
Singapore SMEs run their business on 3–5 disconnected tools. Invoicing in one app. Payroll in another. Inventory in a spreadsheet. Customer orders in a messaging app. At month-end, a finance manager spends two days reconciling them all — manually.
This is not a technology problem. The tools exist. The problem is fragmentation. Each tool is optimised for one task and blind to the others. Stock and finance do not talk. Sales and payroll do not connect. Every integration is a project. Every month-end is a crisis.
INFOC One solves this with a single system for the whole business — finance, payroll, sales, stock, POS, and planning, all connected from day one.
The one sentence: Your business is fragmented. ONE fixes it.
2. Design Principles
These principles govern every decision — from architecture to UX to naming.
Principle 1 — Singapore first, not Singapore adapted
Software built elsewhere and modified for Singapore is not the same as software designed for Singapore from the start. CPF is not a compliance checkbox. InvoiceNow is not a third-party connector. PayNow is not a payment option. They are first-class features built into the core data model.
When CPF rates change, INFOC One updates. When InvoiceNow specifications change, INFOC One updates. The customer does not manage compliance — we do.
Principle 2 — Simple on the surface, rigorous underneath
A Singapore SME owner should be able to assign access to a team member in 30 seconds. The complexity of OPA ABAC policies, Keycloak JWT claims, and Prisma tenant scoping is invisible. The admin sees one dropdown. The engine enforces five layers of isolation.
This is the design contract: make hard things look easy without hiding what makes them hard. The documentation and code expose the full depth. The product surface shows only what the user needs at that moment.
Principle 3 — The 10-second rule
Every screen must be evaluatable by a first-time user in 10 seconds. What is this screen? What is the one thing I should do? What happens if I do it? If a screen cannot answer all three questions in 10 seconds without reading documentation, it is redesigned before shipping.
This rule applies equally to developer-facing APIs. An engineer reading an API endpoint should understand its purpose in 10 seconds.
Principle 4 — One primitive, one responsibility
Every framework primitive in the platform does exactly one thing. ONEKernel defines the data contract — nothing else. ONEStream carries events — nothing else. ONE Build Fields add data — nothing else. ONE Build Hooks react to events — nothing else.
Clean separation means clean isolation. When a primitive is limited to one responsibility, it cannot accidentally break something outside its scope. This is the single most important architectural decision on the platform.
Principle 5 — Earn complexity
Features are revealed as users demonstrate readiness for them. A new tenant sees 5 navigation items. An active tenant with 100 documents sees the full module list. A power user with 90+ days of history sees extensions, API keys, and advanced analytics.
Complexity is not hidden — it is earned. The platform grows with the business.
Principle 6 — Data is permanent. Behaviour is temporary.
When a developer uninstalls an extension, the data that extension created stays. When a customer cancels their subscription, their data is retained for 30 days before any deletion. When a status transition is reversed, the audit trail of the original transition remains.
Data permanence is a trust contract. Businesses depend on their historical records. INFOC One never deletes data without explicit consent.
3. Platform Architecture
3.1 Why microservices with a shared contract
INFOC One is a set of independent NestJS services — one per business domain (ERP, HRM, CRM, POS, WMS, EPM) — that share a common data contract called ONEKernel.
The alternative is a monolith: one codebase, one runtime, all modules sharing the same process. Monoliths are faster to start. They are also harder to scale, harder to isolate failures in, and impossible to give different services different infrastructure requirements.
A microservice architecture with a shared contract gives both: each service is independent (can be deployed, scaled, and updated without touching others), but all services speak the same language (ONEKernel DocumentEnvelope). A finance manager's invoice and an HR manager's payslip are both DocumentEnvelope extensions. They share the same audit trail format, the same AI metadata structure, and the same status state machine.
3.2 Why event-driven communication
Services communicate through Apache Kafka events, not synchronous REST calls between services. When an invoice is approved, the ERP service emits an InvoiceApprovedV1 event. The AI service, the relay service, and any ONE Build Hooks subscribed to that event all receive it independently.
This means: if the AI service is temporarily unavailable, the invoice approval still completes. When the AI service comes back online, it processes the event from where it left off. No data is lost. No user sees an error because a background service was slow.
The rule: REST for queries (reading data that exists). Kafka for events (things that happened). Never use REST to trigger a side effect in another service.
3.3 ONEKernel — why a frozen contract
Every business document in the platform extends DocumentEnvelope. This interface is frozen at v1.0. It cannot be modified without a major version bump and a documented architectural decision.
The reason: a contract that changes is not a contract. If ERPv2 can send an invoice shape that HRMv1 does not understand, the system breaks silently. If ONEKernel v1.0 is frozen, any service that speaks v1.0 can communicate with any other v1.0 service, forever, without negotiation.
Schema evolution is additive only. New optional fields can be added. Existing fields cannot be removed or renamed. This is the same principle that makes REST APIs backward-compatible.
3.4 AI metadata in the data model — not the UI
Every document has an ai_metadata block from day one. This block records: which agent created or modified the document, the agent's confidence score, whether a human reviewed the AI's action, the human reviewer's identity, and a trace ID linking to the full reasoning chain in Langfuse.
This is not a badge on a screen. It is a structural field in the data model. The distinction matters: when an auditor asks "did an AI create this payroll run?", the answer is in the database, not in a log file. When a regulator asks "was a human in the loop?", the answer is in the document itself.
AI provenance is a first-class data concern, not a UI concern.
4. The ONE Naming System
4.1 The rule
Every framework, package, and product starts with "ONE". One word follows that describes the function in plain English — no acronyms, no compound words, no technical jargon.
A developer reads the name and knows the purpose before opening documentation. That is the test.
4.2 Framework names and what they mean
| Name | What it is | Why this name |
|---|---|---|
| ONEKernel | Frozen TypeScript contract | A kernel is what everything else depends on. Non-negotiable core. |
| ONEGuard | Security middleware | Guards protect. Every request runs behind ONEGuard. |
| ONEStream | Typed Kafka events | Events stream between services. |
| ONEClient | Typed API client | How external code talks to the platform. |
| ONEUI | Web component library | The UI layer. The brand surface. |
| ONEACCESS | Access control | Complete control over who sees and does what. |
| ONE Build | Extensibility framework | Developers build on the platform. |
| ONE Ship | Deployment framework | Ship is what teams do when they deploy. |
| ONE Relay | Background job processor | Relays completed work to its destination. |
| ONE AI | Agent runtime | The intelligence layer across all products. |
| ONE Tokens | Design tokens | The design language expressed as code. |
4.3 Package names
All packages are scoped under @one/:
@one/kernel @one/guard @one/stream @one/client
@one/config @one/tokens @one/ui @one/ui-native
@one/access @one/build @one/ship
4.4 Business product names
ONE ERP ONE HRM ONE CRM ONE POS ONE WMS ONE EPM
5. ONEACCESS
5.1 The problem with access control in business software
Most ERP systems give administrators a table of permission checkboxes. There are hundreds. The administrator does not know which boxes to check. They either give everyone everything (insecure) or call a consultant (expensive).
ONEACCESS is designed around a different question: what does a business owner actually need to do? They need to give their finance manager access to invoices and their HR manager access to payroll. They do not need to understand ABAC policies or JWT claims.
5.2 Five layers — visible and invisible
ONEACCESS enforces five layers of access control. Only one is visible to the administrator.
Layer 1 — System (invisible): Keycloak handles authentication. A user either has a valid session or they do not.
Layer 2 — Module (visible as Module Cards): The administrator assigns a role to each team member per module. One dropdown. The module either appears in the sidebar or it does not.
Layer 3 — Action (invisible): OPA evaluates whether the user's role permits the requested action (read, create, update, approve, post, export). The system denies silently — no permission error, the button simply does not appear.
Layer 4 — Record (invisible): withTenant() Prisma scope ensures every query is automatically filtered to the current tenant's data. A user cannot access another company's records, regardless of what they type in the URL.
Layer 5 — Field (invisible): ONEClient masks sensitive fields by role before data reaches any screen. An HR staff member sees SGD ••,••• for salary. A finance manager sees the full amount. This masking happens in the API response transformer — not in UI code — so it applies everywhere: web, mobile, API, exports.
5.3 Why this design
The complexity is in the engine, invisible to the administrator. The administrator's interface is one dropdown per person per module. The OPA Rego policy file is the full specification of what each role can do — but that file is maintained by the engineering team, not the customer.
This is the right separation. Business owners are experts in their business. They should not need to become experts in access control systems to use their ERP.
6. ONEUI
6.1 Why a design system, not just a component library
A component library gives you buttons and tables. A design system gives you a visual language — a set of tokens (colours, spacing, motion, radius) that make every surface feel like it belongs to the same product.
ONEUI is built on Carbon Design System v11 as the engine and Fluent UI v9 as a switchable alternative. Neither is exposed directly to product code. Components reference --one-* tokens. A theme adapter resolves them to whichever system is active. Switching the rendering engine is a one-line configuration change — zero component code changes.
This matters because design system maturity is a moving target. What is best today may not be best in two years. The abstraction layer means INFOC One is never locked to a single design system.
6.2 Singapore components
Standard component libraries are built for global markets. INFOC One's customers are Singapore businesses. ONEUI ships Singapore-specific components that no standard library provides:
CurrencyInput formats SGD correctly — thousands separator, two decimal places, negative values displayed as −SGD 5.00 not SGD −5.00. It uses Decimal(18,2) internally — floating point is not acceptable for financial data.
UENField validates Singapore UEN format on blur and shows a green confirmation or an error. A user never submits an invoice with a malformed UEN.
CPFBracketBadge shows the CPF contribution rates for an employee based on their date of birth. When the date changes, the rates update. The 2025 CPF Board rates are built in.
PayNowQR generates a scannable PayNow QR code from a UEN and an amount. It updates live as the amount changes. It produces a 40mm × 40mm print-ready image for invoices.
PulseCard displays the Business Pulse morning digest with an AILabel badge and a typewriter animation. Each sentence is tappable, taking the user directly to the relevant action.
6.3 The token system
All visual decisions are expressed as --one-* CSS custom properties:
--one-brand-amber: #E8A830 /* The brand. Used sparingly. */
--one-brand-navy: #0A0E1A /* The canvas. Dark primary. */
--one-brand-teal: #1DB8A0 /* Success, confirmation, HRM. */
No component file ever contains a hex value. ESLint enforces this. It is not a suggestion.
7. ONE Build
7.1 Why extensibility matters for an ERP
Every business is different. A wholesale distributor needs a field for supplier lead time. A logistics company needs to trigger a NinjaVan shipment when goods are received. A consulting firm needs to add project codes to invoices. These are not core ERP features — they are customisations that belong to specific businesses.
ONE Build is the framework for these customisations. It gives developers five primitives to extend the platform without modifying its core.
7.2 Design principles for ONE Build
One primitive, one responsibility. Fields touch data. Hooks react to events. Views extend UI. Rules define no-code logic. Packs distribute. None of these overlap.
The platform cannot be broken. A ONE Build Hook runs in an isolated ONE Relay job. A crashing Hook shows an error in the developer's console — not on the customer's screen. A ONE Build View renders inside an error boundary — a crashing View shows an error tile, not a broken page. No Pack can crash the core platform.
Data is permanent. When a developer uninstalls a Pack, the data that Pack's Fields created stays in blueprint_data. It is never automatically deleted. The developer must explicitly request data deletion. This protects customers who may have business records tied to Pack data.
Declared capabilities. Every Pack declares the network domains it will call and the platform resources it will access in pack.json. The sandbox blocks any call to an undeclared domain. This is not trust-but-verify — it is trust-but-enforce.
7.3 Five primitives
Fields add typed, named fields to any ONEKernel document. They are declared in TypeScript, validated at compile time, and stored in a versioned JSON column. A developer cannot add a field to a document at runtime — it must be declared in a Fields definition that passes the build step.
Hooks subscribe to typed ONEStream events and run business logic in response. They receive the typed event and a scoped context — access to the current tenant's data and a declared set of outbound network calls. Nothing more. A Hook runs in ONE Relay — isolated, timeout-enforced, and logged.
Views register React components into named slots in ONEUI. Core screens declare stable named slots (invoice.sidebar, dashboard.card). Views render into those slots with typed document data as props. They use @one/ui components — not arbitrary HTML. They are rendered inside an error boundary.
Rules are no-code logic sequences built in a visual builder by business owners. A Rule is: trigger (an event) → condition (a filter) → actions (a sequence of steps). Business owners build Rules. Developers do not need to be involved. Rules call Hooks internally.
Packs bundle Fields + Hooks + Views + Rules into a signed npm package. Published to the ONE Build Marketplace. Per-tenant monthly pricing. 80% of revenue goes to the developer. 20% goes to INFOC One. Unsigned Packs cannot be installed.
7.4 The Pack manifest
{
"id": "publisher.pack-name",
"version": "1.0.0",
"fields": ["./fields/..."],
"hooks": ["./hooks/..."],
"views": ["./views/..."],
"rules": ["./rules/..."],
"network": ["api.allowed-domain.com"],
"access": ["erp:read"],
"price": { "model": "monthly", "sgd": 29 },
"revenue": { "developer": 80, "platform": 20 }
}
The network array is the complete list of domains the Pack may call. The access array is the complete list of platform resources it may read. Both are enforced at runtime by the sandbox. Anything not declared is blocked.
8. ONE AI
8.1 What AI-native means
There is a difference between a product that has AI features and a product with an AI-native architecture.
AI features: a button that calls an LLM and shows the result. The underlying data model was designed without AI in mind.
AI-native: the data model is designed from the start to record AI provenance. Every document carries ai_metadata. Every status transition records whether it was initiated by a human or an agent. Every agent action is traceable to a specific reasoning chain in Langfuse. The AI layer is structural, not cosmetic.
INFOC One is AI-native. The ai_metadata block on every DocumentEnvelope is frozen at v1.0 alongside the rest of the contract. Removing it would be a breaking change.
8.2 Stateful agents, not chatbots
The AI agents in INFOC One are not chatbots. They do not respond to questions. They run autonomously, triggered by business events, and they produce business outcomes.
The Forecaster runs every morning at 8:00am SGT. It reads the current state of the business — overdue invoices, stock levels, upcoming payroll — and produces a three-sentence digest. It uses LangGraph with PostgresSaver: its state persists across restarts. If the service crashes mid-run, it resumes from where it stopped.
8.3 Human-in-the-loop is not optional
No AI agent in INFOC One can post a GL journal, approve a payroll run, or submit a CPF file autonomously. These transitions require a human to review and approve the agent's proposed action.
This is implemented as a LangGraph interrupt() pause. The agent does its work, proposes an action, and stops. The HITL prompt appears in the user's approval queue. When the user approves, the agent resumes from the exact point it paused.
This is not a safety feature added as an afterthought. It is an architectural constraint that is frozen into the status state machine.
8.4 PII never leaves the premises
Salary data, NRIC numbers, medical certificates, and employee records are routed to Ollama — an on-premises LLM that runs on the customer's own hardware (or INFOC's Singapore servers for cloud customers). These records never reach an external API.
General operations — Business Pulse generation, opportunity scoring, document analysis — route through LiteLLM to Claude Sonnet by default.
The routing rules are enforced in the LiteLLM proxy configuration, not in application code. A developer cannot accidentally send salary data to an external LLM by calling the wrong function. The proxy intercepts the call and routes it correctly based on the data classification in the request metadata.
9. ONE Ship
9.1 Three deployment modes, one codebase
INFOC One runs identically in three configurations:
ship dev: Local development. Docker Compose starts all 12 services, seeds a Singapore chart of accounts, seeds demo data (including an overdue invoice so Business Pulse has something to say on day one), and is ready in 5 minutes. A developer joins the project and is writing code in 15 minutes.
ship self: Customer-hosted Kubernetes. Helm charts and ArgoCD GitOps definitions are provided. The customer's DevOps team deploys to their own infrastructure. AI inference runs on their own GPU via Ollama. No data leaves their network.
ship cloud: INFOC-managed SaaS. Singapore region. Multi-tenant. The same codebase, a different infrastructure profile.
9.2 Smoke tests on every ship
Every deployment — regardless of mode — runs 15 smoke tests before marking done. The tests cover authentication, GST calculation, InvoiceNow XML validation, CPF accuracy, NRIC masking, Business Pulse generation, ONEACCESS enforcement, and infrastructure health.
A failing smoke test blocks the deployment. No exceptions. This is what "tested from day one" means in practice — not a test plan document, but automated checks that run every time.
9.3 Version model
ONE Ship versions match platform releases. v1.0.0 ships with the V1 product. Each version ships an upgrade guide. Self-hosted customers who have a support contract must apply critical compliance patches (CPF rate changes, InvoiceNow specification updates) within 30 days. The upgrade process is documented and tested before each release.
10. Singapore First
10.1 What this means in practice
Singapore compliance is not a module. It is not an add-on. It is built into the core data model and tested against government-published specifications on every CI run.
CPF is calculated using 2025 CPF Board rates for all five age brackets. The Ordinary Wage ceiling of SGD 7,400 is enforced. The Skills Development Levy calculation uses the correct minimum. These calculations are tested against the CPF Board's published contribution tables — not approximated.
InvoiceNow (Peppol PINT-SG) invoice XML is generated from the ONEKernel Invoice document structure and validated against the PINT-SG schema on every test run. INFOC One connects to an IMDA-accredited Access Point Provider to transmit invoices to IRAS.
PayNow QR codes are generated from UEN and amount using the NETS PayNow specification. They appear on every invoice by default and are updated when the amount changes.
IR8A XML is generated in the IRAS Auto-Inclusion Scheme format and submitted electronically by 1 March each year.
NRIC numbers are encrypted at rest using AES-256-GCM with keys stored in OpenBao. They are masked in all API responses, all exports, and all Kafka events. Viewing the full NRIC requires the HR_MANAGER role and creates an audit log entry.
10.2 PSG — the government's endorsement
The Productivity Solutions Grant covers up to 50% of INFOC One's subscription cost for qualifying Singapore SMEs. INFOC One is applying for PSG pre-approval as an IMDA-accredited solution. Once approved, a Singapore SME pays SGD 49.50/month for the Starter plan instead of SGD 99/month.
This is not a discount. It is the Singapore government affirming that INFOC One meets its standards for SME digital transformation tools.
10.3 PDPA by design
The Personal Data Protection Act governs how INFOC One handles employee and customer personal data. INFOC One's PDPA compliance is architectural:
- Data never leaves Singapore (for cloud customers) or the customer's premises (for self-hosted customers) without explicit consent
- NRIC, salary, and medical data are encrypted and access-controlled at the data model level
- Every access to sensitive data is logged to the append-only audit trail
- Soft delete preserves data for the required retention period before any removal
- A Data Processing Agreement is provided to every customer before any personal data is stored
11. What We Will Never Do
These commitments are public. Holding us to them is part of the design.
We will never send Singapore employee data to an external AI service without explicit consent. Salary data, NRIC numbers, and medical records route to Ollama on-prem by default. External LLM routing for sensitive data requires an opt-in at the tenant configuration level, with clear disclosure to the tenant administrator.
We will never introduce a tool with a BSL, SSPL, or proprietary license into the core platform. Every tool in the INFOC One stack is OSI-approved open source. We maintain a public forbidden tools list and the reasons for each exclusion.
We will never make a breaking change to ONEKernel v1.0 without a major version bump and a published migration guide. The contract is the contract. Extensions built on v1.0 will continue to work on v1.0 indefinitely.
We will never use float for financial amounts. Money is Decimal(18,2). Floating-point arithmetic produces rounding errors. Rounding errors in payroll produce incorrect CPF submissions. We use PostgreSQL's DECIMAL(18,2) type and Prisma's Decimal scalar throughout.
We will never hide audit trail entries. The append-only audit log is permanent. Status transitions, agent actions, and sensitive field accesses are logged and cannot be deleted. If an auditor or regulator asks what happened to a document, the answer is in the audit log.
We will never delete a ONE Build Pack's data on uninstall. Fields data lives in blueprint_data on the document. It belongs to the customer, not the Pack developer. Uninstalling a Pack stops its Hooks and Views from running. It does not touch the data.
12. Changelog
This document is version-controlled. Every change is tracked with a date, description, and version number. The full history is available in the Git repository at github.com/infoc/design.
| Version | Date | What changed |
|---|---|---|
| 1.0.0 | April 2026 | Initial publication. Platform architecture, ONEACCESS, ONEUI, ONE Build, ONE AI, ONE Ship, Singapore First, commitments. |
How to contribute
This document describes INFOC One's design thinking as it exists today. As the platform evolves, this document evolves with it.
If you are a developer building on ONE Build and something in this document does not match your experience, that is a bug — either in the platform or in the document. Open an issue at github.com/infoc/design.
If you are considering building on INFOC One and want to understand a decision more deeply, the architecture decision records (ADRs) at design.infoc.one/adr provide the full reasoning behind each major choice.
INFOC ONE Design Document v1.0.0 — April 2026
Published at design.infoc.one
Source at github.com/infoc/design
Maintained by the INFOC One Platform Engineering team