feat: rewrite pass_mgr with build-time AES key, update pcexec & install

pass_mgr:
- Complete rewrite using build-time AES key (injected via ldflags)
- New command format: get-secret/get-username --key, set --key --secret
- Admin commands: init, handoff, init-from (rejected when AGENT_* env set)
- Inline pcguard check for agent commands
- Legacy 'get <key>' kept for backward compat
- Storage: pc-pass-store/<agent-id>/<key>.gpg with AES-256-GCM
- Admin password stored as SHA-256 hash in .pass_mgr/admin.json

pcexec.ts:
- Support new 'get-secret --key' pattern alongside legacy 'get <key>'
- Pass environment to fetchPassword for pcguard validation
- Deduplicate matches, sanitize all resolved passwords from output

install.mjs:
- Generate random 32-byte hex build secret (.build-secret)
- Reuse existing secret on rebuilds
- Pass to go build via -ldflags -X main.buildSecret=<secret>

README.md:
- Document new pass_mgr command format
- Document admin handoff/init-from workflow
- Document security model limitations
- Update project structure
This commit is contained in:
zhi
2026-03-08 21:12:27 +00:00
parent c186eb24ec
commit ddaea57f2d
5 changed files with 798 additions and 658 deletions

View File

@@ -12,7 +12,8 @@
*/
import { execSync } from 'child_process';
import { existsSync, mkdirSync, copyFileSync, chmodSync, readdirSync, rmSync } from 'fs';
import { existsSync, mkdirSync, copyFileSync, chmodSync, readdirSync, rmSync, readFileSync, writeFileSync } from 'fs';
import { randomBytes } from 'crypto';
import { dirname, join, resolve } from 'path';
import { fileURLToPath } from 'url';
import { homedir, platform } from 'os';
@@ -117,14 +118,33 @@ function checkDeps(env) {
// ── Step 3: Build ───────────────────────────────────────────────────────
function ensureBuildSecret() {
const secretFile = join(__dirname, '.build-secret');
if (existsSync(secretFile)) {
const existing = readFileSync(secretFile, 'utf8').trim();
if (existing.length >= 32) {
logOk('Reusing existing build secret');
return existing;
}
}
const secret = randomBytes(32).toString('hex');
writeFileSync(secretFile, secret + '\n', { mode: 0o600 });
logOk('Generated new build secret');
return secret;
}
async function build() {
logStep(3, 6, 'Building components...');
// Generate / load build secret for pass_mgr
const buildSecret = ensureBuildSecret();
// pass_mgr (Go)
log(' Building pass_mgr...', 'blue');
const pmDir = join(__dirname, 'pass_mgr');
exec('go mod tidy', { cwd: pmDir, silent: !options.verbose });
exec('go build -o dist/pass_mgr src/main.go', { cwd: pmDir, silent: !options.verbose });
const ldflags = `-X main.buildSecret=${buildSecret}`;
exec(`go build -ldflags "${ldflags}" -o dist/pass_mgr src/main.go`, { cwd: pmDir, silent: !options.verbose });
chmodSync(join(pmDir, 'dist', 'pass_mgr'), 0o755);
logOk('pass_mgr');