GUI Guide
Desktop starter app: installation, workflows, and implementation notes
Zion Boggan · April 2026 · Oversight Protocol v0.4.5
v0.4.5 introduced a Tkinter desktop starter for users who are not comfortable at the
command line. The GUI wraps the three first-contact workflows every issuer and recipient
needs: generate an identity, seal a file to a recipient, and open a sealed file. The code
is a thin adapter over the oversight_core library; everything it can do is
also available from the CLI. The GUI is a stepping stone
toward the web viewer tracked on the roadmap, not the final
launch surface.
Installation
Tkinter ships with CPython on Windows and macOS. On Debian, Ubuntu, and derivatives it is
packaged separately as python3-tk. Verify that import succeeds before running
the GUI:
python -c "import tkinter; print(tkinter.TkVersion)"
Install Oversight from the repository in editable mode, which registers the
oversight CLI and the oversight-gui entry point:
git clone https://github.com/oversight-protocol/oversight
cd oversight
pip install -e . --user
No additional GUI dependency is required beyond Tkinter. The GUI uses only modules from
the standard library plus oversight_core itself.
Launching the GUI
Two equivalent invocations:
oversight gui # subcommand on the main CLI
oversight-gui # dedicated entry point installed by pip
Both resolve to cli.gui:main. A single Tkinter window opens at
760 by 540 pixels with three tabs: Generate Keys, Seal
File, and Open File. Closing the window exits the process.
Tab 1: Generate Keys
Use this tab to create a new Oversight identity for an issuer or recipient. The identity consists of an X25519 keypair for encryption and an Ed25519 keypair for signing.
| Field | Description |
|---|---|
Identity name | Human-readable identifier stored inside the key JSON. Defaults to alice. Any string is accepted. |
Private key output | Path where the private identity file will be written. Use Choose Output to select a location with the native save dialog. |
Pressing Generate Keypair writes two files next to each other:
- The chosen path, containing the private identity (both X25519 and Ed25519 private halves). The GUI now writes this file with temp-file replacement, requests
0600on POSIX, and applies a best-effort Windows ACL narrowing after the replacement. It refuses Windows device names such asNULand blocks accidental writes over existing Oversight private-key JSON. - A sibling file with the
.pub.jsonsuffix containing only the public halves. This is the file you hand to an issuer who needs to seal something to you.
Tab 2: Seal File
Use this tab to seal a plaintext file to a named recipient. The GUI mirrors the CLI seal semantics: canonical content hash, L3 safety policy, optional L1 and L2 watermarks, passive beacons, and a content fingerprint sidecar are all produced automatically.
| Field | Description |
|---|---|
Input file | Path to the plaintext to seal. |
Issuer private key | Path to the issuer's private identity JSON (the file produced by the Generate Keys tab for the issuer). |
Recipient public key | Path to the recipient's .pub.json file. |
Sealed output | Where to write the .sealed container. If left blank the GUI uses <input>.sealed. |
Registry URL | Registry endpoint recorded in the manifest's policy block. Defaults to https://registry.oversightprotocol.dev. No network call is made from the GUI at seal time; the URL is stored for later open policy checks. |
Content type | MIME type recorded in the manifest. Defaults to text/plain. The content type also informs L3 document-class inference. |
L3 mode | Dropdown with auto, off, boilerplate, full. auto picks the safest mode for the detected document class: full L3 for prose, boilerplate-only for legal or regulatory documents, and off for source code, SQL, logs, and structured data. |
Embed L1/L2 watermarks | Checkbox, on by default. L1 (zero-width Unicode) and L2 (trailing whitespace) do not change visible bytes, so they are safe for every document class. Disabling this checkbox seals the file without any watermarks. |
L3 disclosure prompt
When the selected L3 mode, document class, and content produce a decision that would rewrite body text, the GUI pops a disclosure dialog before proceeding:
L3 semantic watermarking changes visible prose. Continue?
Dismissing the dialog aborts the seal. Confirming applies the L3 transformation, then
embeds L2 and L1 on top of the L3-modified text. This matches the CLI's
--l3-ack gate: the acknowledgement is a structural step, not a checkbox you
forget about. The decision (mode, document class, ack) is recorded in the signed manifest
under l3_policy.
What the GUI writes
- The
.sealedcontainer at the chosen output path, containing the signed manifest, wrapped DEK, AEAD nonce, and ciphertext. - If any watermarks were embedded, a sibling
<output>.fingerprint.jsonwith theContentFingerprint(winnowing hashes plus sentence hashes), the recipient ID, the canonical content hash, and the l3_policy decision. This sidecar is the server-side record the registry can use for attribution if the embedded marks are later stripped. - Seal and open outputs must be different from every selected input path. Existing non-key outputs require confirmation, while private-key JSON targets are hard-blocked.
The manifest also carries a canonical_content_hash, which is a SHA-256 of the
pre-watermark plaintext. If a dispute later turns on whether the recipient received the
exact text of the canonical source, this hash is the anchor the comparison runs against.
Tab 3: Open File
Use this tab to decrypt a sealed file with the recipient's private identity. The GUI verifies the manifest signature, enforces policy (time windows, max_opens, jurisdiction), unwraps the DEK, AEAD-decrypts the ciphertext, and checks the content hash before writing the plaintext to disk.
| Field | Description |
|---|---|
Sealed file | Path to the .sealed container. |
Recipient private key | Path to the recipient's private identity JSON. |
Plaintext output | Where the decrypted plaintext should be written. |
Policy or signature failures surface as a Tkinter error dialog; successful opens show a
small confirmation dialog. The GUI does not print per-layer watermark diagnostics the way
the CLI does. For forensic attribution work, use oversight attribute on the
command line; the GUI is only for the legitimate recipient workflow.
Troubleshooting
| Symptom | Cause and fix |
|---|---|
ModuleNotFoundError: No module named 'tkinter' |
Tkinter is not installed. On Debian or Ubuntu: sudo apt install python3-tk. On Fedora: sudo dnf install python3-tkinter. On Windows and macOS Tkinter ships with the CPython installer. |
GUI launches but oversight is not found |
The package was installed but its scripts directory is not on PATH. Run python -m pip show -f oversight-core | grep bin to locate it, or invoke with python -m cli.gui. |
| Seal dialog reports that the file is not UTF-8 text | The input is binary or not valid UTF-8. Watermarking is defined only over UTF-8 plaintext. Uncheck Embed L1/L2 watermarks to seal without marks, or use the appropriate format adapter from the CLI. |
| Output path is refused as private key material | The GUI detected an Oversight private identity JSON at the output path. Choose a different destination; seal and open never overwrite private keys. |
Open dialog reports PolicyViolation |
The manifest's policy constraints (time window, max_opens, jurisdiction) rejected the attempt. Inspect the manifest with oversight inspect to see which constraint applies. |
Implementation Notes
The GUI is implemented as a single file, cli/gui.py (212 lines), and
consists of one tk.Tk subclass, OversightGui, with a
ttk.Notebook containing three frames. Each frame is built by a
_build_* method and holds its own field variables. The entry point
registered in pyproject.toml is oversight-gui = "cli.gui:main",
and oversight gui resolves to the same main() function through
the main CLI's subparser.
Dependencies
tkinterandtkinter.ttkfor widgets (standard library).tkinter.filedialogfor open and save dialogs (standard library).tkinter.messageboxfor info, error, and yes/no prompts (standard library).oversight_corefor all cryptographic and watermarking operations:ClassicIdentity,Manifest,Recipient,WatermarkRef,seal,open_sealed,content_hash,watermark,l3_policy,beacon, andfingerprint.ContentFingerprint.
Private key hardening
_write_private_json() uses os.open() with
O_WRONLY | O_CREAT | O_TRUNC and mode 0o600 on POSIX so new
private key files are created with restrictive permissions from the start rather than
relaxed and then tightened. On Windows the helper writes the file normally; protect the
enclosing directory with an ACL if the host is shared. This is the same hardening pass
that was applied to the CLI seal path in the v0.4.4 security cycle.
Window sizing and layout
The window is fixed at 760 by 540 pixels. Each tab uses a ttk.Frame with a
grid layout where column 1 expands on window resize so the file-path entries grow with
the window. Browse buttons call tkinter.filedialog.askopenfilename() or
asksaveasfilename() and write the chosen path into the entry's
StringVar.
Scope and non-goals
The Tkinter starter is intentionally small. Features deliberately left out of v0.4.5
include: per-phase attribution diagnostics (use the CLI), registry federation views,
hardware-key integration (planned alongside the Rust KeyProvider trait),
multi-recipient sealing (disabled protocol-wide until the manifest can honestly bind
every recipient), and the drag-and-drop share workflow planned for the web viewer.
If you need any of those, stay on the CLI for now. The roadmap at docs/roadmap.html tracks the web viewer, Outlook add-in, and the broader launch sequence.