<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>zpwrchrome-host — Rust port of browserpass-native + extension actions</title>
<meta name="description" content="Rust port of browserpass-native (PROTOCOL.md v3.1.2) for the browserpass browser extension, plus additive OTP / whole-store search / segmented download manager actions. Single static binary, pure-Rust dependency tree, wire-compatible with upstream Go.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@600;900&family=Share+Tech+Mono&display=swap" rel="stylesheet">
<style>
html { color-scheme: dark; }
* { margin: 0; padding: 0; box-sizing: border-box; }
::selection { background: rgba(5, 217, 232, 0.3); color: #fff; }
:root {
--bg-primary: #05050a; --bg-secondary: #0a0a14; --bg-card: #0d0d1a;
--bg-hover: #12122a;
--accent: #ff2a6d; --accent-light: #ff6b9d; --accent-glow: rgba(255, 42, 109, 0.4);
--cyan: #05d9e8; --cyan-glow: rgba(5, 217, 232, 0.4); --cyan-dim: rgba(5, 217, 232, 0.15);
--magenta: #d300c5; --magenta-glow: rgba(211, 0, 197, 0.3);
--green: #39ff14; --red: #ff073a;
--text: #e0f0ff; --text-dim: #7a8ba8; --text-muted: #3d4f6a;
--border: #1a1a3e;
--cyber-grid-line: rgba(5, 217, 232, 0.042);
--cyber-grid-cross: rgba(5, 217, 232, 0.034);
}
body {
font-family: 'Share Tech Mono', 'SF Mono', 'Fira Code', monospace;
background-color: var(--bg-primary);
background-image:
radial-gradient(ellipse at 20% 50%, rgba(5, 217, 232, 0.045) 0%, transparent 52%),
radial-gradient(ellipse at 80% 20%, rgba(211, 0, 197, 0.04) 0%, transparent 50%),
radial-gradient(ellipse at 50% 82%, rgba(255, 42, 109, 0.035) 0%, transparent 48%),
linear-gradient(var(--cyber-grid-line) 1px, transparent 1px),
linear-gradient(90deg, var(--cyber-grid-cross) 1px, transparent 1px);
background-size: auto, auto, auto, 52px 52px, 52px 52px;
background-attachment: fixed;
color: var(--text);
min-height: 100vh;
line-height: 1.55;
}
.crt::after {
content: ''; position: fixed; inset: 0;
background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(5,217,232,.015) 2px, rgba(5,217,232,.015) 4px);
pointer-events: none; z-index: 9999;
}
.crt::before {
content: ''; position: fixed; inset: 0;
background: radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,.5) 100%);
pointer-events: none; z-index: 9998;
}
.scanline {
position: fixed; left: 0; right: 0; height: 2px;
background: linear-gradient(90deg, transparent 0%, rgba(5,217,232,.03) 20%, rgba(5,217,232,.08) 50%, rgba(5,217,232,.03) 80%, transparent 100%);
box-shadow: 0 0 15px 5px rgba(5,217,232,.04);
pointer-events: none; z-index: 9997;
animation: hscan 12s linear infinite;
}
@keyframes hscan { 0% { top: -2px; opacity: 0; } 5%, 95% { opacity: 1; } 100% { top: 100%; opacity: 0; } }
::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: rgba(5,5,10,.5); }
::-webkit-scrollbar-thumb {
background: linear-gradient(180deg, var(--cyan), var(--magenta));
border-radius: 4px; box-shadow: 0 0 8px var(--cyan-glow);
}
header.hero {
padding: 60px 24px 40px;
border-bottom: 1px solid var(--border);
background: linear-gradient(180deg, #070714 0%, #0d0d22 42%, var(--bg-secondary) 100%);
position: relative;
text-align: center;
box-shadow: 0 4px 28px rgba(0,0,0,.55), 0 1px 0 rgba(5,217,232,.1), inset 0 1px 0 rgba(5,217,232,.06);
}
header.hero::after {
content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 1px;
background: linear-gradient(90deg, transparent, var(--cyan), var(--accent), var(--cyan), transparent);
opacity: .6;
}
h1 {
font-family: 'Orbitron', sans-serif;
font-size: clamp(1.4rem, 4.5vw, 2.6rem);
font-weight: 900;
letter-spacing: 3px;
text-transform: uppercase;
background: linear-gradient(90deg, var(--cyan), #fff, var(--accent), var(--cyan));
background-size: 300% 100%;
-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;
filter: drop-shadow(0 0 12px var(--cyan-glow));
animation: shimmer 6s linear infinite;
margin-bottom: 14px;
}
@keyframes shimmer { 0% { background-position: 0% 0%; } 100% { background-position: 300% 0%; } }
.tagline { color: var(--text-dim); font-size: 14px; letter-spacing: .5px; max-width: 56rem; margin: 0 auto 26px; }
.stat-row {
display: flex; gap: 18px; justify-content: center; flex-wrap: wrap;
margin: 28px auto 0; max-width: 64rem;
}
.stat {
padding: 14px 22px;
background: var(--bg-card); border: 1px solid var(--cyan); border-radius: 2px;
box-shadow: 0 0 16px var(--cyan-glow);
min-width: 8rem;
}
.stat .n { font-family: 'Orbitron', sans-serif; font-size: 26px; font-weight: 900; color: var(--cyan); letter-spacing: 2px; }
.stat .l { font-size: 10px; letter-spacing: 1.5px; color: var(--text-muted); text-transform: uppercase; margin-top: 2px; }
.ctas { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; margin-top: 30px; }
.btn {
padding: 10px 18px; border-radius: 2px;
font-family: 'Share Tech Mono', monospace;
font-size: 11px; font-weight: 600; letter-spacing: 1.5px; text-transform: uppercase;
text-decoration: none; display: inline-flex; align-items: center; gap: 8px;
transition: all .2s;
background-image: linear-gradient(180deg, rgba(255,255,255,.12) 0%, rgba(255,255,255,.02) 40%, transparent 60%);
}
.btn-primary { background: transparent; color: var(--accent); border: 1px solid var(--accent); box-shadow: 0 0 10px var(--accent-glow); }
.btn-primary:hover { background: rgba(255,42,109,.08); box-shadow: 0 0 18px var(--accent-glow); transform: translateY(-1px); }
.btn-secondary { background: transparent; color: var(--cyan); border: 1px solid var(--cyan); box-shadow: 0 0 8px var(--cyan-dim); }
.btn-secondary:hover { background: rgba(5,217,232,.08); box-shadow: 0 0 15px var(--cyan-glow); transform: translateY(-1px); }
main { max-width: 80rem; margin: 0 auto; padding: 36px 20px 60px; position: relative; z-index: 1; }
section.card {
background-color: var(--bg-card);
background-image: linear-gradient(180deg, rgba(255,255,255,.07) 0%, rgba(255,255,255,.02) 30%, transparent 50%);
border: 1px solid var(--cyan);
border-radius: 2px;
padding: 22px 26px;
margin: 22px 0;
position: relative;
box-shadow: 0 0 30px var(--cyan-glow);
backdrop-filter: blur(12px) saturate(1.4);
animation: glow 3.5s ease-in-out infinite;
}
@keyframes glow {
0%, 100% { box-shadow: 0 0 20px var(--cyan-glow), 0 0 4px var(--cyan-glow); border-color: var(--cyan); }
50% { box-shadow: 0 0 40px var(--cyan-glow), 0 0 12px var(--magenta-glow); border-color: var(--accent); }
}
h2 {
font-family: 'Orbitron', sans-serif; font-size: 14px; color: var(--cyan);
text-transform: uppercase; letter-spacing: 2.5px;
padding-bottom: 12px; margin-bottom: 14px;
border-bottom: 1px solid var(--border);
}
.features { display: grid; grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr)); gap: 14px; }
.feature {
padding: 12px 14px;
border: 1px solid var(--border);
border-left: 3px solid var(--cyan);
background: var(--bg-secondary);
font-size: 12px;
color: var(--text);
}
.feature strong { color: var(--accent-light); display: block; font-family: 'Orbitron', sans-serif; font-size: 11px; letter-spacing: 1px; margin-bottom: 4px; text-transform: uppercase; }
.install ol { margin: 0 0 0 1.2rem; }
.install li { margin: 6px 0; font-size: 13px; }
.install code { background: var(--bg-secondary); border: 1px solid var(--border); padding: 1px 6px; border-radius: 2px; color: var(--cyan); font-size: 12px; }
.table-wrap { overflow-x: auto; margin-top: 10px; }
table { border-collapse: collapse; width: 100%; min-width: 50rem; }
th, td { border: 1px solid var(--border); padding: 8px 10px; text-align: left; vertical-align: top; font-size: 12px; }
th { background: var(--bg-secondary); color: var(--cyan); font-family: 'Orbitron', sans-serif; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; }
td code { background: var(--bg-secondary); border: 1px solid var(--border); padding: 1px 6px; border-radius: 2px; font-size: 11px; color: var(--text); }
.muted { color: var(--text-muted); font-style: italic; font-size: 11px; }
.tag { display: inline-block; padding: 1px 6px; font-size: 9px; letter-spacing: 1px; text-transform: uppercase; border-radius: 2px; border: 1px solid var(--cyan); color: var(--cyan); background: var(--bg-secondary); }
.tag.ext { border-color: var(--accent); color: var(--accent); }
pre.code {
margin: 0.5rem 0; padding: 0.85rem 1rem; border-left: 2px solid var(--cyan);
background: var(--bg-secondary); font-family: 'Share Tech Mono', ui-monospace, monospace;
font-size: 12px; color: var(--text); white-space: pre-wrap; word-break: break-word;
line-height: 1.6; overflow-x: auto;
}
pre.code .comment { color: var(--text-muted); }
footer { text-align: center; padding: 24px 16px 40px; color: var(--text-muted); font-size: 11px; letter-spacing: .5px; }
footer a { color: var(--cyan); text-decoration: none; }
footer a:hover { color: var(--accent-light); text-shadow: 0 0 8px var(--cyan-glow); }
</style>
</head>
<body class="crt">
<div class="scanline"></div>
<header class="hero">
<h1>zpwrchrome-host</h1>
<p class="tagline">Rust port of <a href="https://github.com/browserpass/browserpass-native" style="color: var(--cyan);">browserpass-native</a> · PROTOCOL.md v3.1.2 wire-compatible · single static binary · pure-Rust dep tree · <span style="color: var(--accent);">+3 additive extension actions</span> for OTP, whole-store search, and segmented downloads.<br>Cyberpunk HUD palette by MenkeTechnologies.</p>
<div class="stat-row">
<div class="stat"><div class="n">v0.10.2</div><div class="l">version</div></div>
<div class="stat"><div class="n">v3.1.2</div><div class="l">protocol pin</div></div>
<div class="stat"><div class="n">7</div><div class="l">ported actions</div></div>
<div class="stat"><div class="n">3</div><div class="l">extension actions</div></div>
<div class="stat"><div class="n">4,441</div><div class="l">LOC Rust</div></div>
<div class="stat"><div class="n">117</div><div class="l">#[test] fns</div></div>
</div>
<div class="ctas">
<a class="btn btn-primary" href="https://github.com/MenkeTechnologies/zpwrchrome">▸ source on github</a>
<a class="btn btn-secondary" href="https://crates.io/crates/zpwrchrome-host">▸ crates.io</a>
<a class="btn btn-secondary" href="port_report.html">▸ port report</a>
<a class="btn btn-secondary" href="#install">▸ install</a>
<a class="btn btn-secondary" href="#actions">▸ actions</a>
</div>
</header>
<main>
<section class="card">
<h2>what it is</h2>
<p style="font-size: 13px; color: var(--text); margin-bottom: 14px;">
A drop-in replacement for the Go binary that the
<a href="https://github.com/browserpass/browserpass-extension" style="color: var(--cyan);">browserpass</a>
browser extension talks to via Chrome / Firefox native messaging — <strong style="color: var(--accent);">plus</strong>
three additive actions that <code>browserpass-extension</code> doesn't call but are wired into
<a href="https://github.com/MenkeTechnologies/zpwrchrome" style="color: var(--cyan);">zpwrchrome</a>:
TOTP retrieval, whole-store fuzzy + substring search, and a multi-segment HTTP/HTTPS download manager.
</p>
<div class="features">
<div class="feature"><strong>Single static binary</strong>Pure-Rust dep tree: <code>serde</code>, <code>serde_json</code>, <code>ureq</code> with rustls. No aria2, no system OpenSSL, no Go toolchain at runtime.</div>
<div class="feature"><strong>Wire-compatible with upstream</strong>Every action documented in <a href="https://github.com/browserpass/browserpass-native/blob/master/PROTOCOL.md" style="color: var(--cyan);">browserpass-native's PROTOCOL.md v3.1.2</a> is implemented. Error codes 10–32 pin to the same integers; exit code = error code; version reported as <code>3.1.2</code> (packed int <code>3_001_002</code>).</div>
<div class="feature"><strong>Strict 1:1 port discipline</strong>Every <code>src/ported/</code> file mirrors a single upstream Go file. Every Rust fn carries <code>/// Port of <name>() from <go_file>:<line></code>. Every Go comment carries over with <code>// go:NN</code> citation. Identifiers preserved verbatim (<code>DetectGpgBinary</code>, <code>MakeConfigureResponse</code>).</div>
<div class="feature"><strong>OTP <span class="tag ext">ext</span></strong>Shells <code>pass otp <entry></code> with <code>PASSWORD_STORE_DIR</code> set to the matching store. Returns the current TOTP code.</div>
<div class="feature"><strong>Search <span class="tag ext">ext</span></strong>Host-side fuzzy + substring scoring across every configured store. Faster than client-side fzf for large stores.</div>
<div class="feature"><strong>Segmented downloads <span class="tag ext">ext</span></strong>N-segment <code>Range</code> GETs with retry / resume. Chrome cookies + User-Agent forwarded. File-state JSON tracker.</div>
<div class="feature"><strong>Audit-friendly identifiers</strong>Go's PascalCase / camelCase preserved. File-level <code>#![allow(non_snake_case)]</code> makes this an explicit, documented decision rather than a style accident.</div>
<div class="feature"><strong>Drift pinned by tests</strong><code>tests/ported_errors.rs</code> pins every error code to its PROTOCOL.md value. <code>tests/ported_version.rs</code> pins version triple + packed int. <code>tests/ported_integration.rs</code> exercises every action end-to-end against the compiled binary.</div>
</div>
</section>
<section class="card" id="actions">
<h2>ported actions (PROTOCOL.md v3.1.2)</h2>
<div class="table-wrap">
<table>
<thead><tr><th>action</th><th>implementation</th><th>test pin</th></tr></thead>
<tbody>
<tr><td><code>configure</code></td><td><code>ported/request/configure.rs</code></td><td><code>tests/ported_configure_helpers.rs</code> + integration</td></tr>
<tr><td><code>list</code></td><td><code>ported/request/list.rs</code></td><td><code>tests/ported_integration.rs</code> (4 cases)</td></tr>
<tr><td><code>tree</code></td><td><code>ported/request/tree.rs</code></td><td><code>tests/ported_integration.rs</code></td></tr>
<tr><td><code>fetch</code></td><td><code>ported/request/fetch.rs</code></td><td><code>tests/ported_integration.rs</code></td></tr>
<tr><td><code>save</code></td><td><code>ported/request/save.rs</code></td><td><code>tests/ported_integration.rs</code></td></tr>
<tr><td><code>delete</code></td><td><code>ported/request/delete.rs</code></td><td><code>tests/ported_integration.rs</code> (incl. parent cleanup)</td></tr>
<tr><td><code>echo</code></td><td><code>bin/zpwrchrome_host.rs</code></td><td><code>tests/ported_integration.rs</code></td></tr>
</tbody>
</table>
</div>
</section>
<section class="card" id="extensions">
<h2>extension actions <span class="tag ext">additive</span></h2>
<p style="font-size: 12px; color: var(--text-dim); margin-bottom: 10px;">
These are additive — <code>browserpass-extension</code> never sends them, so wire compatibility with upstream is preserved.
They live under <code>src/extensions/</code>.
</p>
<div class="table-wrap">
<table>
<thead><tr><th>action</th><th>behavior</th><th>wire shape</th></tr></thead>
<tbody>
<tr>
<td><code>otp</code></td>
<td>Shells <code>pass otp <entry></code> with <code>PASSWORD_STORE_DIR</code> set to the matching store. Returns the current TOTP code.</td>
<td><code>{action:"otp", storeId, file, settings}</code> → <code>{code:"123456"}</code></td>
</tr>
<tr>
<td><code>search</code></td>
<td>Host-side fuzzy + substring scoring across every configured store. Faster than client-side fzf for large stores.</td>
<td><code>{action:"search", settings, echoResponse:"<query>"}</code> → <code>{matches:[{store, path}, …]}</code></td>
</tr>
<tr>
<td><code>download-start</code> / <code>download-status</code> / <code>download-cancel</code></td>
<td>Multi-segment HTTP/HTTPS downloader. N-segment <code>Range</code> GETs with retry / resume. Chrome cookies + UA forwarded for logged-in URLs.</td>
<td><code>{action:"download-start", url, cookie, segments}</code> → file-state JSON; <code>{action:"download-status", id}</code>; <code>{action:"download-cancel", id}</code></td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="card install" id="install">
<h2>install</h2>
<ol>
<li><code>cargo install zpwrchrome-host</code></li>
<li>Register the binary for an extension ID: <code>zpwrchrome-host --install <ext-id></code> — get the ext-id at <code>chrome://extensions</code> with <strong>Developer mode</strong> enabled.</li>
<li><code>pass</code> support requires GPG configured locally. <code>otp</code> requires <code>pass-otp</code> in <code>$PATH</code>.</li>
<li>Used by <a href="https://github.com/MenkeTechnologies/zpwrchrome" style="color: var(--cyan);">zpwrchrome</a> (a Chrome extension); also a drop-in for the upstream <a href="https://github.com/browserpass/browserpass-extension" style="color: var(--cyan);">browserpass-extension</a> with the OTP / search / download actions silently ignored.</li>
</ol>
<p style="margin-top: 10px; font-size: 12px; color: var(--text-dim);">
Alternative: clone and <code>cargo build --release</code>, then install the resulting binary at the upstream-expected path (<code>$XDG_CONFIG_HOME/<browser>/NativeMessagingHosts/com.github.browserpass.native.json</code>). The <code>--install</code> flag does this automatically per browser.
</p>
</section>
<section class="card">
<h2>port discipline reference</h2>
<p style="font-size: 13px; color: var(--text); margin-bottom: 10px;">
The <code>src/ported/</code> tree is a <strong>1:1 Rust mirror</strong> of the upstream Go source. Discipline borrowed from the
<a href="https://github.com/MenkeTechnologies/zshrs/blob/main/docs/PORT.md" style="color: var(--cyan);">zshrs PORT.md</a> rules, adapted to a Go→Rust port:
</p>
<div class="features">
<div class="feature"><strong>Stem mirror</strong>Every file under <code>src/ported/</code> mirrors a single upstream Go file by stem and relative subpath. <code>errors/errors.go</code> → <code>src/ported/errors/errors.rs</code>.</div>
<div class="feature"><strong>Doc-comment citation</strong>Every Rust fn carries <code>/// Port of <name>() from <go_file>:<line></code>.</div>
<div class="feature"><strong>Inline-comment citation</strong>Every Go inline comment carries over to the Rust port with a <code>// go:NN</code> line-number citation on the corresponding Rust statement.</div>
<div class="feature"><strong>Identifiers verbatim</strong>Go's PascalCase / camelCase identifiers preserved (<code>DetectGpgBinary</code>, <code>MakeConfigureResponse</code>, <code>parseRequestLength</code>). File-level <code>#![allow(non_snake_case)]</code> documents the choice.</div>
<div class="feature"><strong>Local-name verbatim</strong>Local variable names match Go's (<code>gpgPath</code>, <code>normalizedStorePath</code>, <code>parentDir</code>, <code>responseData</code>).</div>
<div class="feature"><strong>No invented helpers</strong>The two recursive store walkers inside <code>list.rs</code> and <code>tree.rs</code> are inlined private fns at the call site — they replace external Go deps (<code>mattn/go-zglob</code>, <code>mattn/go-zglob/fastwalk</code>), not Go-source fns.</div>
</div>
</section>
<section class="card">
<h2>error codes (pinned to PROTOCOL.md)</h2>
<p style="font-size: 12px; color: var(--text-dim); margin-bottom: 10px;">
Every PROTOCOL.md error code pins to the same integer; exit code equals the error code (matches upstream <code>errors.ExitWithCode</code>).
Drift caught by <code>tests/ported_errors.rs</code>.
</p>
<div class="table-wrap">
<table>
<thead><tr><th>code</th><th>name</th><th>cause</th></tr></thead>
<tbody>
<tr><td>10</td><td><code>UnknownAction</code></td><td>Action string not recognized</td></tr>
<tr><td>11</td><td><code>ParseRequestLength</code></td><td>Native-messaging frame length invalid</td></tr>
<tr><td>12</td><td><code>ParseRequest</code></td><td>Request JSON malformed</td></tr>
<tr><td>13</td><td><code>MissingHostsConfiguration</code></td><td>No configured store path</td></tr>
<tr><td>14</td><td><code>InaccessiblePasswordStore</code></td><td><code>~/.password-store</code> unreadable</td></tr>
<tr><td>15</td><td><code>InaccessibleDefaultPasswordStore</code></td><td>Default store path missing</td></tr>
<tr><td>16</td><td><code>InaccessibleGpgHome</code></td><td><code>$GNUPGHOME</code> unreadable</td></tr>
<tr><td>17</td><td><code>InaccessibleDefaultGpgHome</code></td><td>Default GPG home missing</td></tr>
<tr><td>18</td><td><code>InvalidGpgPath</code></td><td>GPG binary not found</td></tr>
<tr><td>19</td><td><code>InvalidGpgHome</code></td><td>GPG home path invalid</td></tr>
<tr><td>20</td><td><code>InvalidPasswordStore</code></td><td>Password store path invalid</td></tr>
<tr><td>21</td><td><code>InvalidStoreId</code></td><td>Store-id not in configuration</td></tr>
<tr><td>22</td><td><code>InvalidPasswordFileExtension</code></td><td>File extension not <code>.gpg</code></td></tr>
<tr><td>23</td><td><code>UnableToListFilesInPasswordStore</code></td><td>Recursive walk failed</td></tr>
<tr><td>24</td><td><code>UnableToDetermineRelativeFilePath</code></td><td>Path-relative computation failed</td></tr>
<tr><td>25</td><td><code>UnableToReadPasswordFile</code></td><td>File-system read failed</td></tr>
<tr><td>26</td><td><code>UnableToDecryptPasswordFile</code></td><td>GPG decryption failed</td></tr>
<tr><td>27</td><td><code>UnableToEncryptPasswordFile</code></td><td>GPG encryption failed</td></tr>
<tr><td>28</td><td><code>UnableToCopyEncryptedPasswordFile</code></td><td>Atomic-rename to target failed</td></tr>
<tr><td>29</td><td><code>UnableToWriteToTemporaryFile</code></td><td>Temp-file write failed</td></tr>
<tr><td>30</td><td><code>UnableToWriteToFile</code></td><td>Final-file write failed</td></tr>
<tr><td>31</td><td><code>UnableToCreateDirectory</code></td><td>Parent directory creation failed</td></tr>
<tr><td>32</td><td><code>UnableToRemoveFile</code></td><td>File-system delete failed</td></tr>
</tbody>
</table>
</div>
</section>
<section class="card">
<h2>example request / response</h2>
<p style="font-size: 12px; color: var(--text-dim); margin-bottom: 8px;">
Native-messaging frame: little-endian 4-byte length prefix + UTF-8 JSON. Same wire format as upstream.
</p>
<pre class="code"><span class="comment"># list every entry in the "personal" store</span>
{"action":"list","settings":{"stores":[{"id":"personal","path":"~/.password-store"}]}}
<span class="comment"># → ok response: tree-shaped list</span>
{"status":"ok","data":{"personal":["work/gmail","work/slack","banking/chase"]}}
<span class="comment"># fetch + decrypt one entry</span>
{"action":"fetch","settings":{"stores":[...]},"storeId":"personal","file":"work/gmail.gpg","echoResponse":""}
<span class="comment"># → ok response: { plain text from gpg --decrypt }</span>
{"status":"ok","data":{"contents":"hunter2\nuser: jacob@menketechnologies.com\notpauth://totp/..."}}</pre>
</section>
<section class="card">
<h2>links</h2>
<ul style="list-style: none; padding: 0; font-size: 13px; line-height: 1.9;">
<li>▸ <a href="https://github.com/MenkeTechnologies/zpwrchrome" style="color: var(--cyan);">github.com/MenkeTechnologies/zpwrchrome</a> · parent extension</li>
<li>▸ <a href="https://crates.io/crates/zpwrchrome-host" style="color: var(--cyan);">crates.io/crates/zpwrchrome-host</a></li>
<li>▸ <a href="port_report.html" style="color: var(--cyan);">port_report.html</a> · Go→Rust port mapping (machine-readable)</li>
<li>▸ <a href="https://github.com/browserpass/browserpass-native" style="color: var(--cyan);">browserpass/browserpass-native</a> · upstream Go reference (PROTOCOL.md v3.1.2)</li>
<li>▸ <a href="https://github.com/browserpass/browserpass-extension" style="color: var(--cyan);">browserpass/browserpass-extension</a> · the browser extension talking to this binary</li>
<li>▸ <a href="https://github.com/MenkeTechnologies/zshrs/blob/main/docs/PORT.md" style="color: var(--cyan);">zshrs PORT.md</a> · the port-discipline rules adapted here</li>
</ul>
</section>
</main>
<footer>
<p>
<a href="https://github.com/MenkeTechnologies">// menketechnologies</a> ·
<a href="https://github.com/MenkeTechnologies/zpwrchrome">zpwrchrome</a> ·
MIT license · pure-Rust dep tree
</p>
</footer>
</body>
</html>