modelbeast/scripts/agent_token.py
m3ultra 19cf9ed21e 🔑 agents get keys: shared agent token minting + guest-visible job outputs
scripts/agent_token.py mints the 'agents' guest user + token non-interactively,
writes data/agent.env (0600, gitignored, never printed). Fixes _register_outputs
dropping user_id — guest job outputs were registered ownerless, so guests could
never see or download their own results.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 23:30:04 +10:00

69 lines
2.5 KiB
Python

#!/usr/bin/env python
"""Mint (or re-mint) the shared agent credential, non-interactively.
Creates the `agents` guest user if missing (random throwaway password — agents
authenticate by token, not password), mints a fresh API token for it, and writes
a ready-to-source env file to data/agent.env (chmod 600, gitignored). The token
is never printed to stdout.
Run from the repo root:
uv run python scripts/agent_token.py # mint, write data/agent.env
uv run python scripts/agent_token.py --revoke # delete ALL of agents' old tokens first
Agents/macs then use:
source ~/Documents/MODELBEAST/data/agent.env # sets MB_HOST + MB_TOKEN
./mb ops
(from another tailnet mac: scp m3ultra:~/Documents/MODELBEAST/data/agent.env .)
The user is a GUEST on purpose: local operators only (flux_local, bg_remove_local,
trellis_mac, sf3d, ...) — all free; the owner's paid fal/OpenRouter keys are
blocked server-side. Mint an owner token from the UI if an agent ever truly needs
a cloud operator.
"""
import argparse
import secrets
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
from server import auth, db # noqa: E402
USERNAME = "agents"
MAX_JOBS = 8 # shared across every agent/mac using this credential
ENV_PATH = ROOT / "data" / "agent.env"
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--revoke", action="store_true",
help="revoke all existing tokens for the agents user first")
args = ap.parse_args()
con = db.connect()
user = auth.get_user_by_name(con, USERNAME)
if user is None:
user = auth.create_user(con, USERNAME, secrets.token_hex(16),
role="guest", max_active_jobs=MAX_JOBS)
print(f"created user '{USERNAME}' (guest, max_jobs={MAX_JOBS})")
else:
print(f"user '{USERNAME}' exists ({user['role']})")
if args.revoke:
n = con.execute("DELETE FROM api_tokens WHERE user_id = ?", (user["id"],)).rowcount
con.commit()
print(f"revoked {n} old token(s)")
raw = auth.create_token(con, user["id"], name="shared-agent-cred")
ENV_PATH.parent.mkdir(parents=True, exist_ok=True)
ENV_PATH.write_text(
"# MODELBEAST shared agent credential — source this file, never commit/paste it\n"
"export MB_HOST=http://100.89.131.57:8777\n"
f"export MB_TOKEN={raw}\n")
ENV_PATH.chmod(0o600)
print(f"token written to {ENV_PATH} (not shown; source it to use)")
if __name__ == "__main__":
main()