Fara í meginmál

A system any Node developer can read and take over

Rekstrarkerfið is one Express application on one PostgreSQL database, built on technology that changes slowly and does not need replacing in a few years. Everything below is taken from the code and the hosting as they run on rekstrarkerfi.is today, and customer installations are built from the same template.

Checked against the code on 19 September 2026 · Everything here is as it runs on rekstrarkerfi.is

One application, one database

Every customer runs the same core: website, shop, point of sale, accounting, users and access control, in one Express application on one PostgreSQL database. Nothing is synchronised between subsystems because there are none; a sale at the till and its ledger entry are rows in the same database.

What is specific to one business is written as an addition beside the core and does not edit core files, so a core update does not overwrite it. Schema changes are migrations in one ordered list in the repo, each applied in its own transaction. A release's migrations have to work with the previous release's code: a column is added in one release and the old one removed in a later release, so the previous image can be put back.

Figure 1. The path of a request (A) and the path of a release (B), as they run today.

Every change is reviewed

The code is written with Anthropic's Claude models, and every change is reviewed before it is merged into the main branch. The contract is with Orange Smiley ehf., and which tool typed the code changes nothing about who answers for it.

Mistakes are expected, from models and from people alike, so no single check is trusted. Automated tests, code review, security rules on every request, a test environment before production and monitoring afterwards each catch what another one misses.

Technology that lasts

Chosen to be dull. Any Node developer can read this codebase on the first day, and none of it needs replacing in two years.

Foundation

Sign-in

Defenses

Operations

28 direct runtime dependencies; npm audit --omit=dev reported 0 known vulnerabilities on 19 September 2026. Exact versions are pinned in the repository and available on review.

Access is checked on the server

Every request passes through these layers before it reaches any data. Access is always checked on the server; the route guards in the browser are a convenience and protect nothing.

  1. pino-http · Access log as JSON. Sensitive fields are redacted.
  2. helmet · The response headers listed below.
  3. cors · Allow-list of origins from ALLOWED_ORIGINS. The server refuses to start without it.
  4. hpp · Repeated query parameters are collapsed to one.
  5. express.json · Every request has a size limit.
  6. sanitizeBody · HTML stripped from every string; rich text through a sanitize-html allow-list.
  7. globalLimiter · Rate limiting on every request, with tighter limits on sign-in, password reset and the contact form.
  8. X-Request-ID · A request id and a trace id are set on every response before anything else can answer. The id is stored with every recorded error.
  9. dbCircuitBreaker · When the database is failing, API calls are refused at once and do not pile up.
  10. requireAuth → requireRole → CSRF · Session, then role, then the CSRF token on authenticated writes. Every admin router checks session and role, and CSRF on everything that writes.
  11. errorHandler · One error shape, {error, code}. The message of a 5xx is never sent to the client.

The headers as served. Check them yourself with curl -sI https://rekstrarkerfi.is

Passwords are never stored

All authentication goes through Lucia on the server. The table shows what happens at sign-in and how access is divided.

Always possible to roll back

A release is a GitHub Actions run started by hand. The Docker image is built once per commit and scanned with Trivy. The test environment runs it first, pinned by its sha256 digest, and once /ready answers there the image is marked tested. Production gets exactly the same image and never builds its own.

Rolling the application back is pinning it to the previous digest again. Migrations that have been applied are not rolled back, which is why they have to be compatible with the previous release's code.

The database is backed up by Azure with point-in-time restore over 14 days, and the backup is geo-redundant.

The core also contains a self-update module. An instance reads a manifest over HTTPS from an allow-listed host, takes the image by its sha256 digest, waits for a maintenance window the customer chooses, refuses a release whose minimum compatible version is newer than the one the instance runs, and verifies itself after boot. It is built and tested, and it will be switched on when the release channel is in place: our own sites first, customers after.

Tested against a real database, and monitored

The Jest integration tests run against a real PostgreSQL that is dropped and rebuilt from the whole migration chain on every run; pg is never mocked. Playwright drives whole user journeys in Chromium, against a throwaway database of its own per branch. A schema test checks that every table the SQL refers to exists. ESLint runs on every commit and push through git hooks.

Automated checks run on a CI server on every pull request: a dependency audit, ESLint, a translation-key check, the unit tests, a smoke set of the integration tests and a build of the production image, which is booted against an empty database. All the integration tests and the Playwright tests run at night when something new has come in, and the same run can be started by hand before a deploy. The working rule is that before a release that adds migrations goes out, the system is booted against an empty database to prove that the whole chain applies.

Server errors (5xx) and browser errors (uncaught exceptions and rejected promises) are recorded in the database with the request id, kept for 90 days and shown on an admin page. An Azure availability test calls the site every five minutes, around the clock, and alerts on a burst of 5xx responses or sustained slow responses are sent by e-mail. /health answers for the process and /ready for the database.

Every customer in their own environment

Every installation is set up from the same template in Azure. The table shows what each customer has to themselves and what is shared.

Decisions, and what comes next

These are decisions we took on purpose, and what is next.

Your website will look good

Design is not a final step added on top of the system. It is part of every page from the start. Your website gets its own look, with your company's colours, type and photos, on a base that is already well made. The same goes for the dashboard your staff use every day. It should be clear, calm and quick to find your way around.

Behind it are people who have designed both spaces and interfaces, from a café in Vestmannaeyjar to interfaces that Microsoft customers use. You do not have to take our word for it. The page you are reading is made the same way.

Questions and answers

The code is written by AI. Who answers for it when something breaks?

Orange Smiley ehf. The contract is with the company and it makes no difference which tool wrote the code. Every change is reviewed before it is merged into the main branch. If something breaks, the previous release can be put back while the fault is fixed.

What catches mistakes, whether AI or a programmer makes them?

So do programmers, which is why the checks are layered and none of them has to be perfect. Tests run against a real PostgreSQL and whole journeys are tested in a browser. A release goes to the test environment before production, and monitoring takes over after that. Migrations are written in two steps so that going back stays possible.

Why not a subscription shop and a separate accounting system?

For many businesses that is plenty, and then there is no reason to switch. This system is for the ones who have started reconciling numbers between systems by hand, or who need a change no app provides. Here everything is in one database, so there is nothing to keep in sync. Changes are part of the service contract and are estimated before any work starts.

Will my custom work break at the next update?

Custom work is written beside the core and does not change core files, so an update does not overwrite it. If it needs the database changed, those changes only add. It is run against the new core in the test environment before the release reaches production, and if something still goes wrong the previous release is put back.

Why no React or other framework? Is this not just home-made?

The frontend is ES modules and the DOM, with no bundler, and that is deliberate. A system meant to last many years then has no framework upgrades and no build chain to go stale, and fewer dependencies means less surface for supply-chain attacks. The security parts come from known libraries: Lucia for sessions, helmet for CSP, csrf-csrf, express-rate-limit and sanitize-html.

Where is the data and who can get at it?

In Microsoft Azure in Sweden, inside the EEA. Every customer has an installation and a database of their own. Access is by role, administrators sign in with two-factor authentication, and what administrators do is logged.

How do I get out, and in what form do I get the data?

The data is the customer's and all of it is in one PostgreSQL database that nobody else shares. It is delivered both as a pg_dump and as CSV files, so another system or another developer can take it from there.

Does customer data go into the AI?

No, not unless the customer asks for it. Data goes to Anthropic only if a customer asks for an API or MCP connection to Claude. Changes are built and tested in the test environment, which is a copy of production refreshed weekly.

Send us the question if the answer is not here. We will answer it and add the answer to this page.

See pricing →

Get a demo