clone

a self-hosted multitenant nostr relay with sites, file storage, git hosting, and inbox and outbox delivery

feat/tinyagent
tinyrelay / labs / hermes / bootstrap.py
bootstrap.py 4.0K
1"""Create identities and seed a disposable Tiny and Hermes workspace."""23import json4import os5from pathlib import Path6import subprocess7import sys8import time910from rpc import Client, public111213def save(path, text, private=False):14 path.write_text(text)15 path.chmod(0o600 if private else 0o644)161718def identity():19 return json.loads(subprocess.check_output(["tinyagent", "keygen"], text=True))202122def initialize():23 os.umask(0o077)24 for directory in ("/public", "/operator", "/hermes-home", "/tiny-data"):25 Path(directory).mkdir(parents=True, exist_ok=True)26 for name in ("owner", "observer"):27 path = Path(f"/operator/{name}.json")28 if not path.exists():29 save(path, json.dumps(identity()), private=True)30 actor = json.loads(path.read_text())31 save(Path(f"/public/{name}.pub"), actor["pubkey"] + "\n")32 home = Path("/hermes-home")33 key_path = home / "tiny-identity.json"34 if not key_path.exists():35 save(key_path, json.dumps(identity()), private=True)36 agent = json.loads(key_path.read_text())37 save(Path("/public/agent.pub"), agent["pubkey"] + "\n")38 env = {39 "TINY_PRIVATE_KEY": agent["secret"], "TINY_RELAY_URL": "http://tiny:7447",40 "TINY_CHANNELS": "lab", "TINY_HOME_CHANNEL": "lab", "TINY_ALLOWED_USERS": public("owner"),41 "TINY_CLI_PATH": "/usr/local/bin/tinyagent",42 }43 save(home / ".env", "".join(f"{name}={value}\n" for name, value in env.items()), private=True)44 config = {45 "model": {"provider": "custom", "default": "tiny-lab", "base_url": "http://model:8080/v1",46 "api_key": "lab-only", "api_mode": "chat_completions", "context_length": 131072},47 "agent": {"max_turns": 12},48 "display": {"busy_input_mode": "queue"},49 "model_catalog": {"enabled": False},50 "tools": {"tool_search": {"enabled": "off"}},51 "platform_toolsets": {"tiny": ["clarify", "code_execution", "terminal", "file"]},52 "platforms": {"tiny": {"enabled": True, "extra": {"rooms": "lab", "allowed_users": public("owner")}}},53 "plugins": {"enabled": ["tinyagent"]},54 "streaming": {"enabled": True, "edit_interval": 0.2, "buffer_threshold": 10},55 "auxiliary": {"title_generation": {"enabled": False}},56 "memory": {"memory_enabled": False, "user_profile_enabled": False},57 "compression": {"enabled": False},58 "terminal": {"backend": "local", "cwd": "/home/hermes/.hermes/workspace"},59 }60 # JSON is a YAML subset; no bootstrap dependency is needed.61 save(home / "config.yaml", json.dumps(config, indent=2) + "\n", private=True)62 (home / "workspace").mkdir(exist_ok=True)63 for path in [home, *home.rglob("*")]:64 if not path.is_symlink():65 os.chown(path, 10001, 10001)66 Path("/public").chmod(0o755)67 os.chown("/tiny-data", 10001, 10001)68 Path("/tiny-data").chmod(0o700)69 print("Disposable identities and Hermes configuration are ready.")707172def seed():73 with Client() as client:74 client.browse("setpolicy", {"writes": "allowlist", "reads": "members",75 "directoryPublic": False, "features": {"files": True}})76 client.publish(0, json.dumps({"name": "Lab operator"}), [])77 if not client.query({"kinds": [9007], "#h": ["lab"], "limit": 1}):78 client.publish(9007, "A disposable workspace for the tinyagent Hermes connector.",79 [["h", "lab"], ["name", "Hermes lab"], ["visibility", "open"]])80 for actor in ("agent", "observer"):81 key = public(actor)82 tags = [["d", key], ["p", key], ["name", f"Lab {actor}"], ["room", "lab"],83 ["jobs", "both"], ["expiration", str(int(time.time()) + 86400)], ["rate", "600"]]84 tags += [["k", str(kind)] for kind in (0, 9, 11, 12, 7, 1111, 40003, 20001, 20002)]85 client.publish(30392, "", tags)86 client.publish(9000, "", [["h", "lab"], ["p", key, "member"]])87 print("Lab room and one-day grants are ready.")888990if __name__ == "__main__":91 {"init": initialize, "seed": seed}[sys.argv[1]]()