Directus intranet panel: registration of waste receipts for the Municipal Waste Collection Point (PSZOK) and the Central Contract Register, with a tree of modules on the left side

Programming

Intranet: Directus today, NocoBase tomorrow

A shared institutional database: 136 collections, from HR and working hours to PSZOK (waste collection point) and contract registers. Why the Directus license forces a switch, how I chose the successor using a prototype, and what the new intranet will look like.

18/10/2026

The intranet I describe here is a system that residents will never see, yet it underpins many services they use. It powers the boards in the hall, PSZOK registration, time tracking, signature generation, and protocol printouts.

It runs on Directus, which is a data management panel built on a standard SQL database. Over several years, it has grown to several modules. Now I am preparing its successor and explaining why.

Directus intranet panel: registration of waste receipts for the Municipal Waste Collection Point (PSZOK) and the Central Contract Register, with a tree of modules on the left side(enlarge)
Intranet panel. Screenshots show a local copy with fictional data

What's Inside

In practice, the intranet is a shared database for the office with a panel to manage it. It has 136 collections of data, over 1500 fields, nearly 400 relationships, and several thousand files. Key modules include:

  • Managing the institution: employees, department and office structure, positions, job scopes, access cards and IDs,
  • Work time tracking: card reader check-ins, daily attendance records, corrections and HR requests,
  • Protection of personal data: register of processing activities, systems, authorizations, risk analysis,
  • PSZOK and waste management system: visits, types of waste, limits, declarations, barcodes,
  • IT: computers, software and licenses, accesses, mobile devices and SIM cards,
  • Finance and assets: budget, expenditure, public procurement, inventory,
  • Shared registers: Central Contract Register with files, contractors, resident card,
  • The investment database published on the municipal portal, serving the Municipal Council and Volunteer Fire Brigades,
  • Screens and kiosks: slideshows, room bookings, and announcements for the lobby boards,
  • Helpdesk, FAQ and SMS gateway settings.
Map of intranet modules with the number of collections: unit management, working time, data protection, PSZOK and waste, IT, finance and property, common registers, investments, Municipal Council, Volunteer Fire Department, screens and kiosks, helpdesk and SMS(enlarge)
Intranet modules and the number of collections in each

Around the Intranet

Most of my systems for the office use the intranet via API. Each with their own account and their own scope of permissions:

  • the RCP importer records swipes from the card reader, and the HR application displays attendance based on them,
  • the PSZOK system reads declarations and records visits,
  • the signature generator takes the employee data and office settings
  • the boards in the hall retrieve a list of rooms,
  • my custom extension generates PDFs from templates, such as PSZOK protocols, using Gotenburg.

What needed to be organized

The intranet grew over years, piece by piece, as needed by each department. Analyzing the structure showed typical results of such growth:

  • the folders in the panel did not correspond to domains, and several collections had no folder at all
  • three different styles of collection names, fields numbered by a generator, and typos fixed in the schema,
  • content statuses in the CMS like 'published' in registers where nothing is published,
  • user account and employee as two separate entities, so the scope of 'my department' couldn't be expressed simply,
  • permissions created on an ad-hoc basis in the panel, without a role template.

I also conducted a security review of the API: who has access to what, which service accounts have overly broad permissions, and what is available without logging in. Permissions were tightened up, and each service now gets its own account with minimal scope.

Directus data model in settings: nested folders and collections(enlarge)
Intranet data model in Directus settings

Licensing Issue

Directus was open-source for years. Since version 10, it operates under the BSL 1.1 license. It is free only for organizations with a budget of less than $5 million per year. The municipality exceeds this threshold several times over, so formally it needs a commercial license.

The panel reminds you of this itself. After the update, every login is preceded by a window asking for the project owner and license acceptance. Version 12 goes further: in the free Core edition, it allows for 3 users and 25 collections. The intranet has 136 collections and several dozen users.

The Directus window requiring the project owner to be designated and the BSL license 1.1 to be accepted, with information about free use below a $5 million budget(enlarge)
BSL License Reminder in the Directus Panel

For the office, this isn't just a minor formality. A paid license for a system used by several other applications ties up the budget for years, and the supplier may change the terms again. This is exactly the kind of dependency I wanted to free the office from.

Choosing a successor

Instead of choosing from descriptions, I built a prototype. Five tools received the same fictional data: 100 employees, 44 departments, 3,000 residents, and 1,500 PSZOK visits. Each also received the same four roles: HR, manager, registry, and regular clerk.

  • Mathesar, with permissions enforced in PostgreSQL by RLS,
  • Teable and Grist are spreadsheet databases
  • NocoBase is a no-code platform with roles, record ranges, and field lists
  • Frappe, the framework on which ERPNext is built.

I tested through the API to ensure each role sees exactly what it should. The manager only sees their own department, the registry has full data on residents, and everyone else only sees a masked PESEL number view. NocoBase won out. The Apache-2.0 license allows for internal use, and the free version includes roles with record ranges, workflow, import/export, and API.

The test migration of the Central Contract Register, which included 2711 contracts, 137 contractors, 300 PDF files and permissions, took around 10 seconds using a script. The values matched exactly to the penny, and the relationships pointed to the same records.

Comparison of the current Intranet on Directus with the planned one on NocoBase and Astro, along with the order of module migration(enlarge)
Migration plan from Directus to NocoBase

How will the new intranet look?

The new intranet is not just a copy of the old one on a different engine. A few principles I established before writing the first line of code:

  • core always enabled (organization, dashboard, dictionaries, data protection, settings) and modules enabled as needed,
  • the schema, roles and personal data classes as code in the repository, with the panel serving only for work and viewing purposes,
  • each field has a data class defined in the module's definition, which determines masking, export, and retention,
  • employee linked 1:1 with an account, so “my department” is a simple filter,
  • the intranet does not store PESEL numbers. A resident is searched for through a link to the domain system, and only what is needed remains in the intranet,
  • everything that is used by someone outside the office, such as the Volunteer Fire Department or resident voting, is a separate application with its own database.

The officials won't be working in the admin panel. They will get their own front end in Astro, compliant with WCAG 2.2, with an accessibility bar and the same look as the gmina portal. The front end communicates with NocoBase using a token of the logged-in user, so the backend, not the browser, enforces the data range and hidden fields.

The login screen of the new internal network interface with an accessibility bar(enlarge)
Logging into the new intranet

Migrating module by module

The new intranet launches alongside the old one. Modules migrate one by one, with one-way data synchronization before switching over. Directus will be turned off once there are no active modules left in it.

  • dictionaries and organizational structure,
  • Central Register of Contracts,
  • PSZOK and waste management system,
  • staff and working hours,
  • other modules,
  • shutting down Directus.

The files go to their own S3 storage on RustFS, thumbnails are generated by imgproxy, and PDF, XLSX, and CSV documents are handled by a separate printing service. Logging will eventually switch to Authentik, which is common across all of the institution's systems.

The most expensive system is not the one you pay for a license of. It's the one that can't be changed because everything else is welded to it.

- Aleksander Kowal

Stack

  • today: Directus 11, MySQL 8, Redis, GraphQL, custom PDF extension,
  • tomorrow: NocoBase 2 on PostgreSQL 16, RustFS (S3), imgproxy,
  • front-end for officials: Astro 7 SSR, TailwindCSS v4, DaisyUI v5, WCAG 2.2 AA,
  • login: Authentik (OIDC),
  • Docker, OneDev CI, deployment from version tag.

Click or scroll to zoom in. Drag to move around.Double tap or pinch to zoom in.