dowrshak zoom in, turning point towards a dedicated credential management tool
Date: 2026 01-January 07
(
cd ~/dev/dworshak
cat README.md
ls
ls src/dworshak/
cat src/dworshak/cli.py
ls -a ~/.dworshak
)
This readme is mid edit
We took a version that said "dworshak should do credentials as part of a tools that does api data transfer orchestration" to now thinking that "clean separation is good, dworshak can be agnostic and puupose built for managing open ssl locally encrypted SQL credentials"
does a tool exist like this already?
It sounds like you are pivoting Project Dworshak toward being a lightweight, file-based "Secret Store" rather than an all-in-one data orchestrator.
The short answer is: Yes, tools like this exist, but they are often either "too big" (enterprise cloud) or "too small" (just a library).
Here is a breakdown of the existing landscape and how Dworshak compares to what’s currently out there.
1. The Direct Competition (CLI Secrets Management)
-
Pass (The Standard Unix Password Manager):
-
How it works: Uses GPG to encrypt individual text files in a directory tree.
-
Pros: Industry standard, extremely secure, git-integratable.
-
Cons: GPG is a nightmare to configure on Windows/Termux compared to your Fernet approach. It doesn't use a structured DB (SQLite), just a folder of files.
-
-
HashiCorp Vault (Local Mode):
-
How it works: The "gold standard."
-
Pros: Does everything.
-
Cons: Massive overkill. It’s a Go binary that runs a server. Your "Anti-Daemon Bias" explicitly rejects this.
-
-
LastPass/1Password CLI:
-
How it works: Cloud-synced CLI tools.
-
Pros: Cross-platform.
-
Cons: Requires an internet connection and a subscription. Dworshak’s "Local-First" / "Data Dam" philosophy is the opposite of this.
-
2. The "Library" Tier (Python-centric)
-
Keyring (Python Library):
-
Most Python devs use the
keyringlibrary, which hooks into the OS Credential Store (Windows Credential Manager or macOS Keychain). -
The Dworshak Edge: OS Keychains are notoriously difficult to script across different environments like Termux or headless Linux servers. By using a local
.keyandvault.db, you are providing portability thatkeyringlacks.
-
3. The Gap Dworshak Fills
You have identified a "Goldilocks Zone" for infrastructure automation:
| Feature | OS Keyring | HashiCorp Vault | Project Dworshak |
|---|---|---|---|
| Storage | System Registry | Memory/Server | Local Encrypted SQLite |
| Portability | Hard (OS specific) | Hard (Server req) | Easy (File-based) |
| Automation | Medium | High | High (CLI/Cron-friendly) |
| Footprint | Tiny | Huge | Small (Python/UV) |
Why Dworshak is still worth building
Even if "Pass" or "Keyring" exist, your specific implementation has three "unfair advantages" for your use case:
-
SQLite Backend: Unlike
pass(which is just flat files), having avault.dballows you to store metadata alongside credentials (e.g., When was this rotated? Which Emerson node does this belong to?). -
Environment Agnostic: By shipping your own "Root of Trust" (the
.keyfile), you ensure that a script written on your Windows dev machine will run exactly the same way on a Termux-equipped tablet or a ruggedized field PLC. -
Scoped for Infrastructure: Most secret managers are for people passwords. Dworshak is designed for system credentials (SOAP/REST tokens) meant to be consumed by other scripts.
Refined Focus for your Readme
Since you are "mid-edit," you might want to lean into this "Separation of Concerns." Dworshak is the Security Guard, not the Delivery Truck. It provides the credentials to the orchestration scripts, but it doesn't necessarily have to move the data itself.
Would you like me to draft a "Changelog" entry for this pivot to a standalone credential manager, or should we refine the register command to handle metadata for those legacy SOAP services?
another question about separation of concerns
an API management tool (let's call it pipeline. or heaphy, for two different credential management tools, of different scale) should have a way to store credentials.
maybe that documented way for both heaphy and pipeline should be to simply say "use the dworshak CLI for clear seaparation of concerns rather than an onboard subcommand. know your service and item names, and call them in scripts or from stored configs"
hypothetically dworshak could expose a library and be imported. This would make it an explicit requirement. Or, we could manaually set up creds using teh dworshak CLI (they have to be set up sometime, we can only automate so much). Then to program would only have to know the ~/.dworshak paths, not the dworshak functions. if it came down to it, in edge cases or other user stories, the CLI could be called using subprocess.run(['dworshak',`setup`]), etc.
compare and contrast one eay, the other, a hybrid, and a way i am not thinking of
This is the classic "Protocol vs. Product" debate in software design. By decoupling the credential store (Dworshak) from the consumer (Pipeline/Heaphy), you are moving toward a Unix-style ecosystem where tools communicate through agreed-upon conventions rather than heavy, built-in features.
Here is the breakdown of the three paths you've identified, plus a fourth "Conventions" approach that might be the cleanest of all.
1. The CLI Dependency (Subprocess Path)
In this model, Pipeline calls dworshak get --service x --item y to retrieve a secret.
-
Pros: Complete language agnosticism. Pipeline could be written in Go, and Heaphy in Python, but they both use the same source of truth. It reinforces Dworshak as a "Security Guard" external to the app.
-
Cons: Performance overhead of spinning up a process. Parsing
stdoutcan be brittle if Dworshak changes its output formatting. -
The "Secret" Risk: Calling a CLI with secrets as arguments is dangerous (they show up in process lists like
psortop), but reading from a CLI’s output is generally safe.
2. The Library Dependency (Import Path)
You package Dworshak as a Python library (pip install dworshak) and Pipeline calls dworshak.get_secret(...).
-
Pros: Type safety, IDE autocompletion, and high performance. It’s the "standard" way Python devs share code.
-
Cons: Strict Coupling. If Pipeline needs Python 3.12 but a legacy version of Heaphy needs 3.10, and Dworshak only supports 3.12, you have a "Dependency Hell" conflict. It also means Dworshak must be Python, limiting future cross-tool use.
3. The Hybrid (The "Tool-Provider" Model)
Dworshak provides a CLI for humans to manage keys, but provides a minimal, stable core library for programs to read them.
-
Pros: Best of both worlds. Humans use the rich Typer CLI; programs use a lightweight C-extension or pure-python reader that has zero dependencies.
-
Cons: You have to maintain two interfaces. You must ensure the "Reader Library" never breaks, even if the "CLI Management Tool" gets new features.
4. The "Agnostic Convention" Path (The way you might not be thinking of)
Instead of the tools knowing about "Dworshak" the software, they only know about The Dworshak Standard.
-
The Idea: You document the storage format (e.g., "Credentials live in
~/.dworshak/vault.db, an SQLite file withschema, encrypted via Fernet using the key in ~/.dworshak/.key"). -
How it works: Pipeline and Heaphy implement their own tiny logic to read that SQLite file directly.
-
Why this wins: This is how the SSH works.
ssh-agentandgitdon't necessarily "import" each other; they both just know where~/.ssh/id_rsalives and how to handle it. Dworshak becomes the manager of the folder, but other tools are "allowed" to look inside the drawer if they know the secret handshake (the encryption logic).
Comparison Matrix
| Feature | CLI/Subprocess | Library Import | Agnostic Convention |
|---|---|---|---|
| Separation | Absolute | Moderate (Shared Env) | Highest |
| Speed | Slow (Process overhead) | Fastest | Fast (Direct DB Read) |
| Language | Any | Python Only | Any (with Fernet lib) |
| Maintenance | Maintain CLI Output | Maintain API/Types | Maintain File Schema |
Recommendation: The "Standardized Path"
Go with a version of Path 4.
-
Dworshak is the only tool that writes to the vault. It owns the "Write" concern (registration, rotation, setup).
-
Pipeline/Heaphy are "Read-Only" consumers. Instead of importing all of Dworshak (and its dependencies like
typerandrich), they use a utility module (perhaps a single filedworshak_reader.pyyou copy-paste or a tiny 0-dependency package) that knows how to unlock thevault.db.
This keeps your "Anti-Daemon" and "Boring Tool" philosophy intact. You aren't building a platform; you're building a filesystem convention with a helper CLI.
Would you like me to show you how a "zero-dependency" reader script for Pipeline would look so it doesn't have to import the whole Dworshak CLI?
"The "Agnostic Convention" Path (The way you might not be thinking of)"
This is exactly what i was thinking of.
That's what i meant when i said "Then to program would only have to know the ~/.dworshak paths, not the dworshak functions"
That makes perfect sense. You’re essentially defining a local protocol. If you go this route, Dworshak isn't just a tool; it's a specification for where secrets live on your infrastructure.
By having Pipeline and Heaphy look directly at ~/.dworshak/, you achieve the ultimate separation of concerns: Dworshak can be updated, rewritten in Rust, or replaced entirely, and as long as the .key and vault.db stay in the same format, the rest of your ecosystem never breaks.
Here is how to solidify that "Agnostic Convention" so it remains robust:
1. The "Contract" (The Schema)
To make this work across different tools, you treat your file structure like an API. You commit to a fixed internal structure for vault.db.
-
Table:
credentials -
Columns:
service(TEXT),item(TEXT),secret(BLOB/TEXT) -
Primary Key:
(service, item)
2. The Implementation (Write vs. Read)
The Writer (Dworshak CLI)
This is what you've already started. It handles the "heavy" dependencies: typer for the UI, rich for the tables, and the logic for password prompts and confirmation dialogs.
The Reader (Pipeline/Heaphy)
Since these tools only need to Read, they don't need the CLI logic. You can create a "Minimalist Reader" pattern. In Python, this would be a single file (e.g., vault_util.py) that you can drop into any project:
Python
import sqlite3
from pathlib import Path
from cryptography.fernet import Fernet
def get_secret(service: str, item: str):
root = Path.home() / ".dworshak"
# 1. Get the Key
key = (root / ".key").read_bytes()
f = Fernet(key)
# 2. Query the DB
with sqlite3.connect(root / "vault.db") as conn:
res = conn.execute(
"SELECT secret FROM credentials WHERE service = ? AND item = ?",
(service, item)
).fetchone()
# 3. Decrypt
return f.decrypt(res[0]).decode() if res else None
3. Why this wins in your "Infrastructure" use case
-
Zero-Dependency Deployment: If you have a legacy machine where you can't install new Python packages, you can still use a simple shell script to
sqlite3query the blob and pipe it to anopensslcommand to decrypt it. -
Version Pinning: Pipeline can pin its "Reader" logic to "Dworshak Schema v1." You can then add v2 features to the Dworshak CLI (like secret rotation or tags) without breaking Pipeline’s ability to read the v1 fields it cares about.
-
Auditability: A security auditor doesn't have to read your Python code to understand the risk. They just need to look at the
~/.dworshakfolder permissions.
4. The Risks of the "Convention"
-
File Locks: Since SQLite handles concurrent reads well, this is rarely an issue. However, you should ensure Dworshak opens the DB in a way that doesn't lock out Pipeline.
-
Key Management: If a user deletes
.keybut keepsvault.db, the data is effectively gone. The CLI should probably handle "Backups" as a core feature.
The "Changelog" Perspective
Since you mentioned this readme is "mid-edit," this pivot is a major architectural milestone.
Would you like me to help you draft the "Architecture" section of the README to define this "Agnostic Convention" for other developers (or your future self) to follow?
since vault_util.py would need to be the same, and it shows the standard for how to access dworshak, it should probably be stabilized into a separate very light library, like dworshak-access, to be included as a dep
a person could just put a vault_util.py file in their code, to reduce dependencies or the need to call an online obfuscated library, but even still having it s a library is good docmetation for what that file should include.
is this the right thinking?
This is exactly the right thinking. You are effectively creating a "Standard Library" for your own infrastructure.
By splitting the project into dworshak (the management CLI) and dworshak-access (the lightweight reader), you are following the "Client-Server" pattern without the overhead of an actual server.
The Architecture of "Dworshak-Access"
To keep this "right," the dworshak-access library should follow a "Zero-Boilerplate" philosophy. It shouldn't have typer, rich, or click as dependencies—only the bare essentials for decryption (cryptography) and the standard library (sqlite3).
Why this is a superior approach:
-
Formalized Schema: If you decide to change the SQLite table structure in the future, you only have to update
dworshak-access. Every tool using that library gets the update automatically via their package manager. -
The "Single Source of Truth": Having a library prevents "Logic Drift." If you copy-paste
vault_util.pyinto five different projects, and then find a bug in how paths are resolved on Termux, you have to find and fix all five files. With a library, you fix it once. -
Documentation by Code: The
dworshak-accesssource code becomes the living documentation for the "Agnostic Convention." If someone wants to write a reader in Go or Rust, they look at that library to see exactly how the Fernet bytes are handled. -
Environment Isolation: Pipeline and Heaphy can remain "clean." They don't need to know how to create a vault; they only need to know how to request a secret.
How the Ecosystem looks now
| Component | Responsibility | Dependencies |
|---|---|---|
| Dworshak (CLI) | setup, register, rotate, list |
typer, rich, cryptography, sqlite3 |
| Dworshak-Access | get_secret(), check_vault() |
cryptography, sqlite3 |
| Pipeline / Heaphy | Data orchestration | dworshak-access |
The "Copy-Paste" Escape Hatch
Even with a library, you should still keep the logic simple enough that a user could copy-paste the core function if they were in a locked-down environment without pip access. You can even include a dworshak-access --snippet command in the main CLI that prints the standalone Python code for a vault_util.py file to the terminal.
Next Step for your README
Since we are standardizing the "Access" side, the README should clearly distinguish between "Management" (using the CLI) and "Consumption" (using the access library).
Would you like me to draft the "Consumption" section for your README, including the code example for how Pipeline would use the dworshak-access library?
(
cd ~/dev/dworshak-access
ls
cat README.md
cat pyproject.toml
ls src
ls src/drowshak-access
cat src/dworshak-access/__init__.py
cat src/dworshak-access/vault_util.py
)
># src/dworshak-access/__init__.py
>from dworshak_accesss.vault_util import get_secret(), check_vault()
>
>__all__ = [
> "get_secret",
> "check_vault"
>]
we need the check_vault function
However you are an AI. I am not sure I trust your architecture for human touch points like central exposed functinos.
make a submission for functions which should be included, in roder of importance, and explain why their design is valid and useful
I appreciate the skepticism. To earn that trust, the "human touch point" design should focus on resilience and diagnostic clarity. If a script fails to get a secret, the developer needs to know why immediately: is the key missing, the database locked, or the service name misspelled?
Here is my submission for the core functions of dworshak-access, ordered by their utility in a real-world pipeline.
1. get_secret(service: str, item: str) -> str
The Primary Workhorse.
-
Design Validity: It should be a "happy path" function that returns a string or raises a specific, descriptive exception.
-
Why it’s useful: This is the only function most programs will ever call.
-
Refinement: It should handle the
Pathresolution and basic decryption logic we discussed. It should raise aSecretNotFoundErrorif the query returns nothing, rather than just returningNone, to prevent downstream code from trying to "authenticate" with aNonetype.
2. check_vault() -> VaultStatus
The Diagnostic Safety-Check.
-
Design Validity: Instead of returning a Boolean (True/False), it should return a structured object or a NamedTuple explaining the state of the
.dworshakdirectory. -
Why it’s useful: Before a long-running data sync starts, the program can call
check_vault(). If it fails, the script can exit gracefully with a message like: "Vault found, but .key file permissions are incorrect." -
What it checks:
-
Presence of
~/.dworshak/. -
Readability of
.key. -
Integrity of
vault.db(can it run a simplePRAGMA schema_version?).
-
3. list_items(service: Optional[str] = None) -> List[Tuple[str, str]]
The Discovery Tool.
-
Design Validity: Returns metadata (Service/Item names) only. Never returns the actual secret blobs.
-
Why it’s useful: If a developer is working in an interactive REPL or a notebook, they can run
list_items()to remember if they named the credentialrjn_api/primaryorrjn_api/admin.
Proposed Implementation for vault_util.py
Python
import sqlite3
import os
from pathlib import Path
from typing import NamedTuple, List, Tuple, Optional
from cryptography.fernet import Fernet
DEFAULT_ROOT = Path.home() / ".dworshak"
class VaultStatus(NamedTuple):
is_valid: bool
message: str
root_path: Path
def check_vault(root: Path = DEFAULT_ROOT) -> VaultStatus:
"""Diagnose the health of the Dworshak environment."""
if not root.exists():
return VaultStatus(False, "Vault directory does not exist.", root)
if not (root / ".key").exists():
return VaultStatus(False, "Security key (.key) is missing.", root)
if not (root / "vault.db").exists():
return VaultStatus(False, "Credential database (vault.db) is missing.", root)
try:
with sqlite3.connect(root / "vault.db") as conn:
conn.execute("SELECT 1 FROM credentials LIMIT 1")
except sqlite3.Error as e:
return VaultStatus(False, f"Database error: {e}", root)
return VaultStatus(True, "Vault is healthy.", root)
def get_secret(service: str, item: str, root: Path = DEFAULT_ROOT) -> str:
"""Retrieve and decrypt a specific secret."""
key_path = root / ".key"
db_path = root / "vault.db"
if not key_path.exists():
raise FileNotFoundError(f"Dworshak key not found at {key_path}")
fernet = Fernet(key_path.read_bytes())
with sqlite3.connect(db_path) as conn:
cursor = conn.execute(
"SELECT secret FROM credentials WHERE service = ? AND item = ?",
(service, item)
)
row = cursor.fetchone()
if not row:
raise KeyError(f"No credential found for {service}/{item}")
return fernet.decrypt(row[0]).decode()
Why this design is "Human-Centric"
-
Explains Failure: Using
VaultStatusand descriptiveKeyError/FileNotFoundErrormessages ensures that when a cron job fails at 2:00 AM, the logs tell you exactly which file was missing. -
Path Flexibility: By allowing a
rootargument (defaulting to~/.dworshak), you make the library testable. You can point it at a temporary directory during CI/CD tests without touching your real production credentials. -
No Magic: It doesn't try to "auto-fix" anything. If the vault is broken, it tells you. This fits your "Data Dam" philosophy—predictability over cleverness.
Would you like me to generate a Changelog for dworshak-access reflecting these diagnostic additions?
service name and item id
please explain the typical expecations and common knowledge and even the history around the service name and item id pairing. I first was exposed to it using python keyring, and i have inspected windows credential manager, which is shaped a little differently. is the service name and item id pattern a common database artifact? which did it come into existance? how did it spread? what are the alternatives? is it stable and expected? how can it be best leveraged properly?
The (service, item) pairing is a foundational pattern in identity management. It is a implementation of Namespacing, a concept used to prevent collisions when multiple applications share a single storage backend.
1. The History: From Global to Scoped
In early computing, credentials were often stored in global files (like /etc/passwd) or application-specific flat files.
-
The Problem: If two different apps both needed a "password" for "admin," they would overwrite each other in a shared registry or flat file.
-
The Solution: The Service/Account (or Service/Item) architecture emerged to create a unique composite key. This was popularized by the PAM (Pluggable Authentication Modules) architecture in the 1990s and later formalized by desktop "keychain" services.
2. Is it a "Database Artifact"?
Technically, no. It is a Schema Pattern.
In database terms, this is a Composite Primary Key. In the dworshak vault, your primary key is defined as UNIQUE(service, item). This ensures that while you might have ten "primary" items, you only have one "primary" item per "service."
3. Comparison of Implementations
| System | Terminology | Structure | Philosophy |
|---|---|---|---|
| Python Keyring | Service / Name | API-driven abstraction | Cross-platform middleware. |
| macOS Keychain | Service / Account | Attributes in a Keychain Item | Deeply integrated into the OS; supports metadata. |
| Windows Cred. Mgr. | Internet/Network Address | "Target Name" | Originally built for NTLM/Network auth; feels "flat" compared to Nix. |
| Project Dworshak | Service / Item | Namespace / Entry | Portable, SQLite-backed, local-first. |
4. How to Leverage it Properly
The "Service" name should describe the Target System, while the "Item" should describe the Role or Identity.
-
The Service Name: Should be broad enough to encompass all credentials for that API.
-
Good:
rjn_api,emerson_sov,wastewater_mirror_db -
Bad:
my_script_v1(This ties the secret to the consumer, not the source).
-
-
The Item ID: Should describe the specific credential type or user level.
-
Good:
primary,readonly,admin,api_token -
Bad:
password(Too generic if you ever need to add a second set of keys).
-
5. Alternatives
The primary alternative is a UUID-based system or Path-based system.
-
Path-based (HashiCorp Vault): Uses a filesystem-like path:
secret/data/infrastructure/rjn/primary. -
UUID-based: A random string like
550e8400-e29b.... This is stable but human-unreadable without a lookup table.
6. Stability and Expectations
The (service, item) pattern is extremely stable. By sticking to this, Dworshak follows the "Principle of Least Astonishment"—a developer familiar with Python's keyring or Linux's secret-service will immediately understand how to use your tool without reading a manual.
Implementation for Dworshak
In your vault_util.py, I recommend enforcing a lowercase, slug-friendly format for these names (e.g., service.lower().strip()) to prevent a situation where RJN_API and rjn_api are treated as different services.
(service, item) architecture: its longevity is largely due to how well it maps to real-world infrastructure. In automation, you are rarely just dealing with "a password"; you are dealing with a specific authority (the service) and a specific grant or identity (the item).
By adopting this, Dworshak isn't just a side project—it's speaking the same language as professional-grade security systems while maintaining the simplicity of a local SQLite file. It makes the system intuitive for anyone who has touched a Linux keyring or a modern API vault.