LedgerMap · design options · 17/10/2026

Lock LedgerMap to one PC, with no user accounts

Three ways to make the desktop app run only on PCs that you approve. Each option has a diagram, its strong and weak points, and the work it needs. The recommendation and a rollout plan come at the end.

1Locked to one PCA copy of the app on another PC must not work.
2No loginOn an approved PC, the app simply opens.
3You approve each PCOnly you, as admin, give a PC permission to run the app.

First, the honest truth about hardware locks

A check inside the app is weak

The user controls their PC. A skilled person can edit the .exe to skip a check, or fake a hardware ID. No hardware lock that runs only on the PC is safe from this.

A check on our server is strong

LedgerMap already needs our server. It reads PDFs, matches ledgers and keeps the saved work. If the server serves only approved PCs, an edited app gets nothing. The server is our real lock.

Option 1 · Recommended

Device key with server approval

The PC makes a secret key inside its security chip. The key can never leave that PC. You approve the key once. After that, the PC proves who it is every time the app starts.

What the user sees on a new PC

 Ledger Map

This PC is not approved yet

Send this code to your LedgerMap contact.

4827 1936
Your name or company
Asmark Engineers
Copy codeSend request
Waiting for approval. The app opens by itself.

What you do

$ ssh ledgermap-server ledger-map-admin devices CODE NAME ASKED STATUS 4827 1936 Asmark Engineers 17/10 11:42 pending $ ledger-map-admin approve 48271936 \ --customer "Asmark Engineers" Approved. The app on that PC opens now. $ ledger-map-admin revoke 48271936 Revoked. That PC stops at its next request.

Later, the same list can be an admin screen in the desktop app. A faster start is also possible: you make a one-time 8-digit setup code for a customer. The first PC that enters it is approved at once, and the code then stops working.

Strong points

  • A copy on another PC cannot work. The key cannot leave the chip.
  • No login. The app signs in by itself at each start.
  • You approve each PC, and you can revoke one at any time. The change works at once.
  • An edited .exe does not help. The server refuses any PC it does not know.
  • Ready for the paid plan. Add "paid until" and "number of PCs" to the customer.

Weak points

  • It needs the internet. LedgerMap needs it already, so nothing new here.
  • Some old PCs have no TPM chip, or it is turned off. Those PCs use a key locked with Windows DPAPI instead. A full disk clone could copy that key.
  • A new Windows install or a new motherboard means a new key, so you approve that PC again.
  • It is the most work of the three to build.
Lock strength StrongWork about 3 PRsRunning cost noneWorks offline no
Option 2

Licence file locked to a hardware ID

The app reads the PC's hardware (motherboard ID, disk serial, Windows ID) and shows a Machine ID. You sign a licence file for that ID. The app checks the file each time it starts, without the internet.

Strong points

  • Simple to understand. No server work to check a licence.
  • Works offline. This only matters for apps that do not need a server.
  • Uses the signing method we already use for app updates (Ed25519).

Weak points

  • Only the app checks the lock. An edited app or a faked hardware ID gets past it.
  • It does not protect the server. We would still need Option 1 or passwords for that.
  • You cannot revoke a file. It works until it expires.
  • A new disk or motherboard changes the ID, so the user needs a new file.
  • Sending files back and forth by hand is slow for users.
Lock strength WeakWork about 2 PRsRunning cost noneWorks offline yes
Option 3

Licensing service (Keygen, Cryptlex, LicenseSpring)

A company that sells licence systems does the PC locking. It gives you a dashboard for customers, PCs and seat limits, and later for payments. Our server must still check that service's tokens.

Strong points

  • Ready-made: PC limits, offline grace days, expiry and a dashboard.
  • Connects to payments (for example Stripe) when LedgerMap becomes paid.
  • Their team keeps the licence system working, not ours.

Weak points

  • Two systems to keep in step: the service and our server, which still needs its own check.
  • A monthly cost that grows with the number of customers.
  • Customer names and PC details go to another company.
  • If the service is down, nobody can start the app.
  • Most of these services use a PC fingerprint, not a chip key. So copying is easier than in Option 1.
Lock strength Medium – strongWork about 2 PRs + vendor setupRunning cost monthly feeWorks offline with grace days

Side by side

1 · Device key + approval2 · Licence file3 · Licensing service
Copy to another PC is stoppedYes. The key cannot leave the chip.Partly. Only the app checks.Mostly. Fingerprint only.
No login for the userYesYesA licence key once
You approve each PCYes, with one command or a setup codeYes, by signing a fileYes, in their dashboard
Revoke a PC at onceYesNo. It works until it expires.Yes
Protects our serverYesNoOnly if our server checks tokens
An edited .exe gets inNoYesNo, when our server checks
Data stays with usYesYesNo
Running costNoneNoneMonthly fee
Ready for paid plansYes. Add expiry and PC count.Expiry onlyYes, with payments
Work to buildMost (about 3 PRs)Least (about 2 PRs)Medium + vendor setup

How hard is it to get around the lock?

1 · Device key + approval
Strong
3 · Licensing service
Good
2 · Licence file
Weak

Recommendation: Option 1

LedgerMap already depends on our server, so the server is where the lock belongs. Option 1 meets all three requirements. It costs nothing to run, keeps customer data with us, and later becomes the paid plan without a rebuild. Option 3 is worth a look later, only if you want a ready-made payment and licence dashboard.

Rollout plan

Server: customers and devicesA customer is an account with no password. Each customer has one or more approved PCs (public keys). New API routes: ask for approval, get a challenge, sign in with a signature. Admin commands: devices, approve, revoke, setup-code. The server records each request, approval and revocation in usage tracking.
Desktop app: the key and the approval screenThe Go side makes the key in the TPM chip, or with DPAPI if there is no chip, and signs in by itself. The login page becomes the approval screen shown above. The browser never sees the key; the session cookie stays in Go, as it does today.
Move everyone acrossExisting accounts become customers, so their parties and imports stay. Each PC asks once, and you add it to the right customer. Older apps keep password login until a required update moves them over. Then password login closes.

Build log

Updated as the work goes on. Nothing merges into main until you approve it.

#138 · Device access (collects all parts) → mainWaits for your reviewIntegration branch device-lock. Part 1, the contract (docs/device-access.md), is its first commit. PR #138
Part 2 · ServerMerged into device-lockDevices, challenges, setup codes, admin commands, rate limits, tracking, PASSWORD_LOGIN switch. PR #140. All server checks pass, including PostgreSQL integration tests (full flow, setup code used once under 4 parallel requests, revoke ends sessions, limits, cleanup). OpenQodex: passed, no findings.
Part 3 · Desktop app and approval screenIn progressTPM key (DPAPI fallback), automatic sign-in, approval screen, profile changes. A second sub-agent builds it in parallel.
Part 4 · RolloutPR open, merges after part 3Rollout steps in docs/device-access.md, clearer message when password login is closed, version 0.4.0, and a fix for a race in the admin commands. PR #141.
Independent review (Fable)Not startedA Claude Fable sub-agent reviews the whole branch. Findings are fixed before you review.
  1. You chose Option 1 with 8-digit numeric codes.
  2. Wrote the shared contract (wire format, routes, admin commands). OpenQodex review: passed, no findings. Opened PR #138.
  3. Started two sub-agents in their own worktrees: one for the server, one for the desktop app and the screen.
  4. Server part merged (PR #140): server/device.go, server/device_admin.go, schema changes, admin commands devices / approve / revoke / setup-code / customers.
  5. Contract gap: the contract did not say what a malformed public key returns. The server answers 400 device_key_invalid. The desktop side must handle it; this is checked in the review.
  6. Small gaps the server agent decided: a revoked pending request is never cleaned up; --customer refuses a name that already exists; requests that keep racing can get 409 after 5 tries. All documented in docs/device-access.md.
  7. OpenQodex's golangci-lint scanner failed to run (exit 2). The reviewer itself ran and passed the change.
  8. Wrote part 4 (PR #141). Rollout order: deploy server → give existing accounts setup codes, or approve with --user <email> to keep their data → publish 0.4.0 as a required update → set PASSWORD_LOGIN=off.
  9. OpenQodex found a race: two admin commands at the same moment could create two customers with one name. Fixed with a database lock.
  10. OpenQodex flagged that version 0.4.0 must not ship without the desktop code. So PR #141 merges only after part 3.
  11. The first PostgreSQL test run used wrong credentials. A rerun with the local development database settings passed.

Questions you may have

Is an 8-digit number safe enough?

The request code (PC → you) is only a label for a pending request. The chip key holds the security. The server picks the number at random, no two pending requests share it, and the list shows the name and time beside it. So 8 digits are plenty.

The setup code (you → PC) is a secret. It has 100 million choices, works once, expires after 24 hours, and the server allows only a few wrong tries per address and about 100 wrong tries per hour in total. Against one code, that gives a guesser roughly a 1 in 40,000 chance. Bank OTPs use 6 digits with the same kind of limits.

The user gets a new PC or reinstalls Windows. What then?

The app makes a new key and shows a new code. You approve it for the same customer, and all their work is there. You revoke the old PC.

Can one customer use two PCs?

Yes, if you allow it. Each PC has its own key, and all of them belong to one customer. They share the same parties. When LedgerMap becomes paid, the number of PCs can be part of the plan.

What if a PC has no TPM chip?

All Windows 11 PCs have one. Most Windows 10 PCs from about 2016 on have one too, but it is sometimes turned off. Without it, the key is locked with Windows DPAPI for that PC. Copying the app folder still does not work, but a full disk image clone could carry the key. The server can mark these PCs so you can see them.

Can someone just copy the app folder or the installer?

Yes, but the copy is useless. The key lives in the chip (or in DPAPI), not in the app folder. On the new PC the app has no key and shows a new pending request.

Can an attacker spam the server with requests?

Requests are rate-limited per address, and a pending request expires after a few days. Pending PCs cannot use the API until you approve them.

What changes when we start to charge?

Add "paid until" and "PCs allowed" to each customer. At sign-in, the server checks them and tells the app when a plan has expired. There is no new lock to build.

Words used on this page

TPM chip
A small security chip on the motherboard. It can make keys that never leave it.
DPAPI
A Windows feature that encrypts data so only that PC can decrypt it. It is the fallback when there is no TPM.
Public / private key
A pair of keys. The private key signs and stays secret on the PC. The public key checks signatures and can be shared with the server.
Challenge
A random value that the server sends. The PC signs it, so an old signature cannot be used again.
Fingerprint / Machine ID
A value made from hardware serial numbers. It is easy to read, so it is also easy to fake.