NOTE Local-first · Vue · Privacy
Local-first tools for sensitive data
Design principles for browser-only utilities that inspect sensitive exports without turning the application into another data processor.
Some utilities are valuable precisely because they do less. A tool that helps organize a password-vault export does not need an account system, a sync service, analytics, or a database. Each added service creates another place where sensitive material can travel, persist, or leak.
Vaultsort uses a browser-only model for unencrypted Bitwarden and Vaultwarden JSON exports. The application can inspect folders, edit entries, and review duplicates without uploading the export to a backend.
Start with a strict data boundary
“Local-first” can mean several things. For a sensitive-file utility, the useful definition is concrete: imported contents stay inside the current browser context unless the user explicitly exports a new file.
That boundary affects architecture:
- File selection uses browser file APIs.
- Parsing and validation run in the client.
- Editing state lives in memory.
- Export is initiated by the user.
- No backend endpoint accepts vault data.
A privacy statement should describe this behavior directly. It should not rely on broad words such as “secure” without explaining where the data goes.
Validate before editing
A local tool still handles untrusted input. Imported JSON can be malformed, unexpectedly large, or structurally different from the supported export format.
const parsed: unknown = JSON.parse(source);
if (!isSupportedVaultExport(parsed)) {
throw new Error('Unsupported vault export format');
}
The application should treat parsed JSON as unknown until structural checks establish the fields needed by the editor. Validation errors should identify the unsupported shape without echoing sensitive field values into logs or error reports.
Make irreversible actions obvious
Deleting a duplicate, moving an item, or renaming a folder changes the next exported file. Those operations need clear state transitions even when no server is involved.
Useful safeguards include:
- A review state before destructive bulk changes.
- Counts that explain how many entries are affected.
- Undo while the working session remains open.
- An explicit export action rather than silent file replacement.
The browser sandbox prevents some classes of server-side leakage, but it does not make confusing product behavior safe.
Privacy mode is a presentation control
A screen can expose credentials to someone nearby even when network behavior is perfect. Privacy mode addresses that separate risk by masking sensitive values during review or demonstration.
This distinction matters: privacy mode does not encrypt the imported file, and local processing does not make an unencrypted export safe to leave on disk. The interface should communicate both limits.
Internationalization changes layout assumptions
Vault data can contain any language, and the surrounding tool may need full right-to-left support. RTL is more than reversing text alignment. Direction affects navigation order, icon placement, disclosure controls, and the visual meaning of movement.
A component should derive directional behavior from the active locale instead of scattering one-off CSS overrides:
const direction = computed(() => (locale.value === 'ar' ? 'rtl' : 'ltr'));
Logical CSS properties such as margin-inline-start and inset-inline-end then keep the layout coherent in both directions.
Avoid accidental network paths
A browser-only promise is weakened by unrelated third-party requests. Remote analytics, hosted fonts, error-reporting payloads, and externally loaded assets can all create network activity while sensitive data is open.
The safest default is a self-contained production build with a restrictive Content Security Policy. If an external service is genuinely necessary, document exactly what it receives and ensure vault contents cannot enter its payload.
Test the promise, not only the interface
UI tests should cover importing, organizing, duplicate review, privacy mode, RTL, and export. The architecture promise also needs verification:
- Production code contains no upload endpoint.
- Normal editing emits no application network requests.
- Reloading clears in-memory sensitive state unless persistence is an explicit feature.
- Exported JSON preserves supported fields and valid structure.
Local-first is an architectural constraint, not a visual label. The product earns that label when data flow, dependencies, interface decisions, and verification all support the same boundary.