meta-creds.sh: operator CLI for Meta credential store
This commit is contained in:
@@ -0,0 +1,32 @@
|
|||||||
|
# Meta Credential Store (operator-only)
|
||||||
|
Centralized encrypted store for Muse, Instagram, Facebook account credentials.
|
||||||
|
## Location (VM only)
|
||||||
|
- `/etc/netvm/meta-credentials/store.age` — age-encrypted JSON (600 root)
|
||||||
|
- `/etc/netvm/meta-credentials/.age-key` — age private key (600 root)
|
||||||
|
- `/usr/local/bin/meta-creds.sh` — CLI (700 root)
|
||||||
|
## Usage
|
||||||
|
```bash
|
||||||
|
sudo meta-creds.sh list muse # list account IDs (no secrets)
|
||||||
|
sudo meta-creds.sh get muse <id> # output JSON (never log this)
|
||||||
|
sudo meta-creds.sh add muse <id> # interactive prompts
|
||||||
|
```
|
||||||
|
## Schema
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"muse": {
|
||||||
|
"<id>": {
|
||||||
|
"email": "...", "phone": "...",
|
||||||
|
"age_verified": "true",
|
||||||
|
"instagram_linked": "<handle>",
|
||||||
|
"verified_by": "human", "verified_at": "2026-10-03T...",
|
||||||
|
"notes": "..."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"instagram": {"<id>": {"username": "...", "password": "...", "email": "..."}},
|
||||||
|
"facebook": {"<id>": {"email": "...", "password": "..."}}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Rules
|
||||||
|
- Operators only. Developers never get access (prevents board leaks).
|
||||||
|
- Decrypt transiently, never log values, never put in chat/memory.
|
||||||
|
- Human validates Instagram linking; operators automate after.
|
||||||
Executable
+47
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Meta credential store CLI (operator-only)
|
||||||
|
# Usage:
|
||||||
|
# meta-creds.sh add <type> <id> # interactive add (prompts for fields)
|
||||||
|
# meta-creds.sh get <type> <id> # output JSON to stdout (never log)
|
||||||
|
# meta-creds.sh list <type> # list IDs (no secrets)
|
||||||
|
# Types: muse, instagram, facebook
|
||||||
|
STORE_DIR="/etc/netvm/meta-credentials"
|
||||||
|
STORE="$STORE_DIR/store.age"
|
||||||
|
KEY="$STORE_DIR/.age-key"
|
||||||
|
if [ ! -f "$STORE" ] || [ ! -f "$KEY" ]; then
|
||||||
|
echo "error: store not initialized" >&2; exit 1
|
||||||
|
fi
|
||||||
|
decrypt() { sudo age -d -i "$KEY" "$STORE" 2>/dev/null; }
|
||||||
|
encrypt() {
|
||||||
|
PUBKEY=$(sudo grep "public key" "$KEY" | awk '{print $4}')
|
||||||
|
sudo age -r "$PUBKEY" -o "$STORE.tmp" 2>/dev/null && sudo mv "$STORE.tmp" "$STORE" && sudo chmod 600 "$STORE"
|
||||||
|
}
|
||||||
|
case "$1" in
|
||||||
|
list)
|
||||||
|
decrypt | jq -r ".$2 | keys[]" 2>/dev/null || echo "(empty)"
|
||||||
|
;;
|
||||||
|
get)
|
||||||
|
decrypt | jq ".$2[\"$3\"]" 2>/dev/null
|
||||||
|
;;
|
||||||
|
add)
|
||||||
|
TYPE="$2"; ID="$3"
|
||||||
|
echo "Adding $TYPE/$ID (fields as JSON, empty to skip):"
|
||||||
|
TMP=$(mktemp)
|
||||||
|
decrypt > "$TMP" 2>/dev/null
|
||||||
|
# Build entry via prompts
|
||||||
|
ENTRY=$(jq -n '{}')
|
||||||
|
for field in email phone username password notes age_verified instagram_linked verified_by; do
|
||||||
|
read -p "$field: " val
|
||||||
|
if [ -n "$val" ]; then
|
||||||
|
ENTRY=$(echo "$ENTRY" | jq --arg v "$val" ".$field=\$v")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
ENTRY=$(echo "$ENTRY" | jq ".verified_at=\"$(date -u +%FT%TZ)\"")
|
||||||
|
jq --arg t "$TYPE" --arg id "$ID" --argjson e "$ENTRY" '.[$t][$id]=$e' "$TMP" | encrypt
|
||||||
|
rm -f "$TMP"
|
||||||
|
echo "added $TYPE/$ID"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "usage: meta-creds.sh {list|get|add} <type> [id]"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user