Platform

Availableapp.erp.io/<module>

Architecture

Every module in the suite is a separate application, deployed separately, mounted on a path of app.erp.io. The single origin is the design.

Origin
One
Applications
Twelve, plus the shell
Session
One cookie
Exception
Client Portal

One origin, many applications

Modules used to live on their own subdomains — accounting.erp.io, crm.erp.io, and so on. They have been collapsed onto paths of a single origin: app.erp.io/accounting, app.erp.io/crm. The old subdomains still answer and redirect, permanently, with the path and the query string preserved.

ModuleMountOld address
Accounting/accountingaccounting.erp.io
CRM/crmcrm.erp.io
Projects/pmpm.erp.io
Marketing/marketingmarketing.erp.io
Phony/phonysdr.erp.io
Chat/chatchat.erp.io
PLM/plmplm.erp.io
Sign/signsign.erp.io
CFO/cfocfo.erp.io
Canvas/canvascanvas.erp.io
Legal/legallegal.erp.io
Client Portalportal.erp.io— not collapsed

What one origin buys

  • One session cookie. A module that has never seen you can tell the suite already has, and sign you in without asking.
  • No cross-site hand-off. Browsers are increasingly hostile to third-party cookies and cross-site redirects. There is no cross-site anything here.
  • Same-origin data. One module reading another is a plain fetch, not an integration.
  • One bookmark. People remember app.erp.io and navigate by the rail.
  • One certificate, one WAF policy, one set of security headers.

The cost is that a path is global to an application, so mounting a module rewrites every URL it generates. That is invisible when it is right and produces a specific, recognisable class of bug when it is wrong — a link that drops the mount and lands on the shell. If you ever see a module link take you to the shell home page, that is what happened, and it is worth reporting with the URL.

The exception: Client Portal

The Client Portal is deliberately not collapsed onto a path. It answers on eleven hostnames — portal.erp.io plus ten account.<brand> domains that clients sign in to — and a path mount is global, so mounting it would rewrite every one of those brand addresses at once and break every bookmark and emailed link a client holds.

The reason to collapse a module is a session cookie shared with the shell, and the portal's users are clients who never touch app.erp.io. So the trade does not apply. That decision is revisited only if the portal can be deployed twice from one image with the mount driven by configuration.

app.erp.io/accounting/reconciliation
One origin, three applications
app.erp.iothe shell
/home /settings /adminshell routes
/accounting/*Accounting
/crm/*CRM
/pm/*Projects
/.well-known/jwks.jsontoken verification
The same origin, three modules. Only the first path segment changes.

Independent deployment

Each module is its own application with its own database and its own release cadence. A change to Accounting does not rebuild Chat. Modules do not share tables and there are no foreign keys across them — where two modules need the same fact, one asks the other over HTTP and holds a copy.

That is why a module can be down while the rest is up

A module failing is not the suite failing. The rail still renders, other modules still work, and the failure is scoped to one path. It also means "is erp.io down" is usually the wrong question — the useful one is which path.

What this does not do

No on-premise deployment

The suite is hosted. There is no installable build.

No customer-chosen region

Everything runs in one region today. Data residency is not selectable.

No module-level status page

There is a single status surface, not one per module.

Questions

Are the old subdomains going away?

They redirect permanently and there is no plan to remove them. Bookmarks keep working.

Can we self-host one module?

No. Modules depend on the shell for identity and are not distributed separately.

Do modules share a database?

No. Each has its own, with no cross-module foreign keys.