WorkCtx

Security

This tool holds other people’s credentials. The description below is meant to be read by somebody deciding whether to let it — so it says how each part works, and it says what each part does not do.

The vault

One master password. It is stretched with Argon2id into a key-encryption key, with the memory and time cost measured on the machine the vault is created on rather than assumed. That key wraps two data keys, and every secret is encrypted under one of them with AES-256-GCM, each cell bound to its own row and column so a ciphertext cannot be moved to a different account and still open.

The vault file is 144 bytes. It carries the KDF parameters, a salt, a nonce and the two wrapped keys, and nothing else. There is no recovery key and no reset — forgetting the master password loses the vault, and that is a property of the design rather than an omission.

What unlocking gives you

The two data keys have different jobs and different lifetimes. The repository key reads client names, sites and usernames; it is held until the agent exits or the screen locks (the screen-lock half is Windows-only so far; on Linux it lasts until the agent exits). The secrets key reads passwords and TOTP seeds; it expires on an idle timer and a hard cap, both configurable.

The login screen asks for the repository half. Copying a password asks again. This means that opening the console to see which account goes with which client does not leave every secret unlocked for the rest of the afternoon.

This is retention, not a second authorisation boundary. One password derives one key-encryption key and unwraps both data keys. Whoever can reach the names can reach the secrets by typing the same password again. What the split buys is how long the interesting half is in memory, not who is allowed to see it.

When a secret is copied, the agent does the copying. The plaintext goes from the vault to the clipboard inside the agent process and never appears in the browser page, so a page cannot be induced to leak it. The clipboard is cleared after a set interval, and only if nothing else has overwritten it in the meantime.

What is on disk

Every column that identifies anybody is encrypted: context names, slugs, client and vendor organisations, site URLs, usernames, notes, and the detail field of the audit log. Lookups by slug use a keyed blind index, so a record can be found without decrypting the table.

After the encryption pass, the database is vacuumed. This was learned rather than planned: SQLite leaves freed pages intact, and a search of a freshly sealed file for a client’s name found it in a page nothing referenced any more.

A snapshot is taken before any migration that destroys data. On an installed copy it stays beside the database, under the same account and the same permissions, as the route back from a bad migration. On a portable drive it is never written to the drive at all — see below.

What is on the stick

A portable drive holds the sealed database, the vault file, and one encrypted archive per client for the browser profile. The archives are named for the context’s id, not the client, so a directory listing says nothing. The drive is expected to be exFAT, which carries no file permissions, which is why everything on it is encrypted rather than relying on the filesystem to refuse anyone.

The browser never runs from the drive. Opening a context unseals its profile into a working copy on the host; closing it seals the copy back to the drive and then removes it, never the second step without the first. A working copy that outlives its browser — a crash, a pulled drive — is written back at the next start if it can be, and discarded if it cannot, because a client’s live cookies left on a borrowed machine are the worse outcome.

The one plaintext the drive’s own housekeeping produces — a snapshot taken before the encryption pass — goes to the host’s temporary directory, never the drive, and is removed the moment the pass has committed. What a pulled drive strands there is swept by wctx profiles --purge from any later machine.

A drive can never remember its master password. The option to skip the prompt exists only for an installed copy, where it is bound to the operating-system account, and it is refused outright on removable media.

The agent and the console

The agent listens on loopback only and talks to nothing else. There is no update check, no crash reporting and no telemetry, and there will not be a licence check that phones home either. The console is a static page the agent serves from disk, on the same origin, with a Content-Security-Policy that pins the hashes of its own scripts; its session token rides in the URL fragment, which is never sent to the server and never lands in a log.

A tripwire in wctx doctor checks whether the machine has been enrolled into a client’s tenant. That is the one condition that invalidates the premise of the tool rather than merely breaking a feature, and it is reported as such.

What it does not protect against

  • Malware running as you. It can read this process’s memory or simply talk to the agent’s socket. This is inherent to every password manager.
  • A shared login. If a remembered repository key is turned on, anyone logged in as you can read client names and usernames without the master password. That is the same bargain as a browser’s “stay signed in”; it is off by default and says so where it is offered.
  • A malicious host, for the duration. While a context is open on a borrowed machine, its profile is plaintext on that machine’s disk. The design keeps that window short and cleans up afterwards; it does not pretend the window is zero.
  • Tampering with the binaries. They are not yet code-signed. Every build goes out with its SHA-256 for exactly this reason.

Verifying a download

Every build goes out with its SHA-256 beside it. Until the binaries are signed, comparing that hash with the file you received is the only check available, and it is worth the thirty seconds.

# Windows
certutil -hashfile wctx-0.1.0.zip SHA256

# Linux
sha256sum wctx-0.1.0.zip

This page describes the software as built. If something here stops being true, the page is wrong and should be fixed before the software is.