An experiment apart from plastron
The primary work here is plastron. Plastron is a polyglot reactive substrate (a spreadsheet kernel and a desktop shell, hosted at plastron.ca). This HRIS prototype is a separate experiment. It is a self-contained experiment with CRDT SQLite (cr-sqlite) for offline-first data. This data must merge correctly after a day of offline changes.
Today, plastron.ca hosts plastron, and a server stores the data. The goal is still the zero-server, offline-capable build in one file. (Offline plastron has no OPFS at this time.)
Before we add CRDT SQLite to the full kernel, we wanted a smaller test case with difficult conditions:
- Protected B personnel rows
- encrypted portable dumps
- convergence between many operators
- no network egress
This build gave us lessons about changeset export, the merge procedure, audit tables, and CSP-safe inlined WASM. The plan is to put these lessons into plastron as a future cr-sqlite integration. They will not replace the spreadsheet model.
The offline HRIS is a practical test of a capability that will be necessary for plastron. The Juárez names and data are only the story for the demo.
The problem
A community police office must plan its staffing. The plan must answer these questions:
- What positions exist?
- Who holds each position?
- Where are the gaps?
- How much payroll pressure is there?
Enterprise HRIS products assume a connection that is always on, identity federation, and a vendor security boundary. A neighbourhood command post, a borrowed laptop, or a planning cell that works from USB drives has none of these.
The design question was direct. Can several planners keep one consistent set of personnel data with no server, no public internet, and no plaintext rows on disk at any time?
The idea
The idea is to supply one file: a built index.html.
The file embeds React, SQLite, and the cr-sqlite CRDT extension as inlined WebAssembly.
Operators double-click the file. The live database exists only in memory.
Only encrypted blobs go to disk.
These blobs are the morning truth.hrisdump file and the evening lastname.hrischanges delta files.
The Content Security Policy sets connect-src 'none'.
Thus, even if an attacker takes control of script execution, the page has no fetch, WebSocket, or beacon path off the machine.
This is a deliberate trade-off. It is good for an air-gapped daily procedure. It makes SaaS analytics impossible.
The demo shows Juárez PD: Zona Centro, Anapra, fictional badge numbers, and invented salaries. There are two reasons. The original spreadsheet model came from a Canadian police HR layout. We wanted a story that is clearly not a production system. The operation of the app does not depend on the jurisdiction. Only the labels and the seed data change.
The daily cycle
The daily cycle uses the same model as git, but for staffing data. Each planner changes a local copy, and then the coordinator merges the changes. Two things are different from git. The app encrypts the merged file. The coordinator can do the merge with only a browser and no administrator rights. Figure 1 shows the cycle.
Morning — one truth file
- The coordinator distributes
truth-YYYYMMDD.hrisdump. - The coordinator gives the organization passphrase for the day through a separate channel, never together with the file.
- Each planner opens the HTML file on the local machine.
- Each planner decrypts the truth file.
- Each planner passes the user gate: the planner selects an identity from a pre-provisioned operator list.
- The app records a
session_openevent and writes amorning_db_versionwatermark.
Day — offline work
- Planners create, read, update, and delete (CRUD) officers, positions, and assignments.
- The dashboard updates the deficits and the headcount immediately.
- Each change adds a row to
change_event. The row records who made the change, what changed, and when. - Each change also updates the
updated_bycolumn of the changed row. - cr-sqlite records the changes of each replica in
crsql_changesfor the merge. This record is separate from the attribution to a person. - The app saves nothing to disk automatically. If a planner closes the tab before an export, the planner loses the work. This behaviour is intentional.
Evening — merge
- Each planner uses Export my changes to make an encrypted
.hrischangesfile. This file is not a full database dump. - The coordinator opens the same HTML file and loads the morning truth file.
- The coordinator uses Merge operator changes on the Data & Security screen to apply each delta file.
- cr-sqlite merges the changes, and the result does not depend on the order. Changes that conflict converge by the CRDT rules. The merge does not use last-writer-wins file replacement.
- The coordinator exports the merged truth file for the next day.
An optional Node script, merge-hris.mjs, exists for automation. The script is not necessary to operate the cycle.
CRDT solves data convergence. It does not automatically solve accountability. For this reason,change_eventandsession_logare full tables in the database, and they sync through the same merge mechanism.
Protected B and CSE cryptography
Personnel files are not open data. They contain names, assignments, and compensation. In the Government of Canada categories, personnel files are usually Protected B. Protected B means that unauthorized disclosure can cause serious injury outside the national interest. See the TBS Standard on Security Categorization (Appendix J) and the 2023 direction on personal information in the aggregate.
A community police office that handles officer PII has the same injury model. This is true even when the formal framework is municipal privacy law and not Treasury Board policy.
Technical requirements that the app obeys
For encryption algorithms, CSE publishes ITSP.40.111 (Cryptographic algorithms for UNCLASSIFIED, Protected A, and Protected B information). For a file at rest, the dump format uses the strongest practical set of algorithms from that document:
- AES-256-GCM — bulk encryption with authenticated integrity (ITSP.40.111 §3.1, §4.2)
- PBKDF2-HMAC-SHA-256 at 250,000 iterations — key derivation from the passphrase (ITSP.40.111 §10.9). The minimum in the GC password guidance is 10,000 iterations.
- Per-file random salt and IV — a 16-byte salt and a 12-byte GCM nonce
- Non-extractable Web Crypto keys — JavaScript can never read the raw key material
The Directive on Security Management, Appendix B §B.2.3.4–§B.2.3.6 states the rules for storage and transit of sensitive electronic media. Data at rest, in transit, and on portable media must have protection that agrees with its sensitivity. Encryption is necessary on public networks and on networks with risk. This app does the at-rest part for portable dumps. Coordinators must still have a procedure for USB handoff or for approved encrypted channels.
What the app enforces in software
- The app has no export path for plaintext
.sqliteor.csvfiles. - The database is only in memory while the session is open. The lock erases the passphrase and the database.
- The app has an operator identity gate.
- The
change_eventandsession_logtables are append-only. - The app exports encrypted changesets. Thus many planners can sync, and no full dumps compete.
What the organization must do
AES alone does not make an app correct for Protected B. The other requirements are for the organization, not for the software. The browser cannot implement the controls in this list. A jurisdiction must still have these documented controls before operational use with real officers:
- Privacy Impact Assessment. The federal reference is the Directive on Privacy Impact Assessment. A municipality uses the equivalent in its local FOIP/IP legislation.
- Threat and risk assessment, and authorization to operate. These follow the Policy on Government Security lifecycle. The organization accepts the residual risks (a lost USB drive, shoulder surfing, out-of-date truth files).
- Need-to-know roster. The roster says who is in
app_user. The organization makes sure, outside the app, that each person has security screening. - Passphrase ceremony. The organization distributes the key for the day through a separate channel. It rotates the key after a compromise. It never stores the key adjacent to the dumps (this includes cloud-sync folders).
- Media policy. USB handoff is the preferred method. The policy says if OneDrive or Google Drive can hold ciphertext.
- Breach response plan. The plan uses the Privacy Breach Management Toolkit. The plan uses PIN 2022-01 for cyber incidents that involve personal information.
- Coordinator SOP. The SOP includes the morning distribution, the evening collection, and the review of the merge audit. It also tells the coordinator what to do about missed exports and out-of-date watermarks.
- Operator training. Operators learn to export changes before the lock. Operators learn that the user selection is not MFA.
The project documents put these requirements in two separate groups: the build requirements in the app, and the governance procedure requirements. We deliberately delayed the governance group until the prototype becomes stable.
Why use a browser tab?
The rules of a community office can prohibit the installation of server software or database engines on managed PCs. A static HTML file with inlined WASM is unusual. But it has properties that enterprise software rarely gives together:
- offline-first operation
- auditable merge semantics
- ciphertext-only persistence
- zero network egress, and CSP enforces this
The GitHub Pages host
(demo) is a convenience for evaluation.
The intended deployment is different.
You download the file and run it locally on a controlled machine, from file:// with no backend.
Status
The repository rheophile10/offline-Protected-B-HRIS includes these items:
- Playwright user stories
- an automated CRDT convergence test
- the screen-recorded demonstrations below (also in the separate how-to guide)
The project examines how much of a personnel HRIS can go into one encrypted, mergeable, offline file. It is not a production authorization. It is not a separate branch of the product roadmap.
The next step for plastron is to put these CRDT SQLite patterns into the substrate. Then cels, sheets, and shared state can keep offline changes and merge them in the same way. If this experiment continues, the next steps for it are:
- governance documentation for a real pilot
- an optional PIN for each operator
- a legal review for the specific jurisdiction
How to use the app
Each video below shows a Playwright test from
tests/hris.spec.ts.
The test runs on the real built app, and a screen recorder records it.
The recording has an 800 ms slow-motion delay between actions, so each step is easy to follow.
The how-to guide
of the repository includes the same videos.
USER-STORIES.md
contains the user stories.
The README
contains the setup notes and the build notes.
index.html
and double-click it. Then each procedure below works offline.
A server, an installation, and administrator rights are not necessary.
Start a session US-1
You open the app, load the data for today with a passphrase, and select your operator identity.
- Open the file. The app shows the locked gate first.
- Keep the Demo data selection.
- Enter a passphrase.
- Click Start session.
- Select an operator. The dashboard shows 27 officers and the staffing gaps.
Add an officer US-2
You add a new hire. The roster and the dashboard update, and the audit log attributes the change.
- Open Officers.
- Click + Add officer.
- Enter the badge and the name.
- Click Save. The headcount increases. The app attributes and audits the change.
Assign an officer to a position US-3
This procedure keeps the staffing levels and the vacancy deficits current.
- Open Assignments.
- Click + Assign officer.
- Select the officer and the position.
- Click Assign. The staffing views recompute automatically.
Query data and export an encrypted CSV file US-4
You run ad-hoc SQL and share the numbers. No plaintext goes to disk at any time.
- Open SQL Console.
- Write a query.
- Click Run.
- Export the result. The export makes only a
.csv.encfile. - Decrypt the file later from Data & Security.
Export changes and merge them (no Node) US-5
The coordinator merges the offline changes of all operators in the same HTML file.
- Operator: click Export my changes. The app makes an encrypted
.hrischangesfile. - Coordinator: open the morning truth file.
- Coordinator: click Merge operator changes.
- Coordinator: select the delta files. CRDT makes the divergent changes converge.
- Coordinator: click Export merged truth to make the truth file for the next day.
Recruit and hire an applicant US-7
You move applicants through the recruitment stages and change a successful applicant into an officer.
- Open the Recruitment kanban.
- Add applicants.
- Move an applicant through the stages to Offer.
- Click Hire →. The app creates an officer record and an active assignment. The headcount updates.
Training and compliance US-8
You see the certifications that expire, and you renew certifications.
- Open Compliance.
- Read the Expired, Expiring, and Valid counts and the firearms-current ratio.
- Filter by KPI.
- Click Renew or + Record certification.
Leave and absence US-9
You monitor the leave requests, the approvals, and the persons who are on leave today.
- Open Leave.
- Read the counts: on-leave today, pending, and upcoming.
- Click + Request leave to add a request.
- Click Approve or Deny on each pending row.
Workforce planning US-10
You see the projected vacancies before the retirements occur.
- Open Planning.
- Read the projected deficit at the +1/+2/+3/+5-year horizons.
- Read the list of officers who are near the pension milestone. Use this list for hire-ahead decisions.
Audit trail US-11
You see who changed each item. This attribution stays after the merge.
- Open Audit.
- Read the change log (operator, entity, action) and the session events.
The logs are CRRs. They go with each changeset into the merge.
Officer file US-12
The officer file shows emergency contacts, performance reviews, and conduct records in one place.
- Open Officer File.
- Select an officer.
- Read the contacts, the reviews, and the conduct records.
- Add a review or update a disposition.
Equipment register US-13
You monitor who holds each firearm, vehicle, radio, or camera.
- Open Equipment.
- Read the in-service, issued, and available counts.
- Click Issue or Return for an asset.
- Mark an asset as Maintenance or Retired.
Lock the session US-6
You erase the decrypted data from memory when you go away from the machine.
- Export your changes first. The app discards unsaved work, and this is intentional.
- Click Lock session. The app goes back to the gate. It erases the database, the key, and the identity from memory.
The full guide is a separate page: rheophile10.github.io/offline-Protected-B-HRIS/how-to.html
References
- CSE ITSP.40.111 — approved algorithms (AES, GCM, PBKDF2)
- CSE ITSAP.40.016 — encryption for sensitive data at rest and in transit
- Policy on Government Security
- Directive on Security Management (Appendix B — IT security controls)
- Standard on Security Categorization (Protected A / B definitions)
- Direction on security categorization of personal information in the aggregate (2023)
- Guideline on Password Security (PBKDF2, offline attack countermeasures)
- cr-sqlite — convergent replicated SQLite
- offline-Protected-B-HRIS — source code and build
- plastron — the primary project. Offline operation and CRDT SQLite are in the plan.
- plastron.ca — the hosted build (a server stores the data today, and the goal is zero servers)