diff --git a/src/psyc/cli.py b/src/psyc/cli.py
index fbdccf8..7ae4096 100644
--- a/src/psyc/cli.py
+++ b/src/psyc/cli.py
@@ -17,11 +17,15 @@ from psyc.lines import classify, courier, lookup, proof, respond, route, scout,
from psyc.lines import map as map_line
from psyc.models import Outcome
from psyc.result import Err, Ok
+from psyc._federation_cli import register as _register_federation_cli
+from psyc._pulse_cli import register as _register_pulse_cli
app = typer.Typer(add_completion=False, help="psyc — defensive CTI routing & sealing")
log.configure()
_log = log.get(__name__)
+_register_pulse_cli(app)
+_register_federation_cli(app)
@app.command("init")
diff --git a/src/psyc/cockpit/app.py b/src/psyc/cockpit/app.py
index eef212d..14196f4 100644
--- a/src/psyc/cockpit/app.py
+++ b/src/psyc/cockpit/app.py
@@ -12,7 +12,7 @@ from fastapi.templating import Jinja2Templates
from starlette.middleware.sessions import SessionMiddleware
from psyc import db, log
-from psyc.cockpit import adminauth, case_visuals, docker_view, inference, journey as journey_view
+from psyc.cockpit import adminauth, case_visuals, docker_view, federation_routes, inference, journey as journey_view, pulse_routes
from psyc.lines import courier as courier_line
from psyc.lines import ledger as ledger_line
from psyc.lines import lookup as lookup_line
@@ -35,6 +35,9 @@ app = FastAPI(title="psyc Operations Cockpit", version="0.1.0")
app.add_middleware(SessionMiddleware, secret_key=adminauth.session_secret(), max_age=3600)
app.mount("/static", StaticFiles(directory=str(HERE / "static")), name="static")
+pulse_routes.register(app, TEMPLATES)
+federation_routes.register(app, TEMPLATES)
+
def _admin_ok(request: Request) -> bool:
return bool(request.session.get("admin_ok"))
diff --git a/src/psyc/cockpit/static/sw.js b/src/psyc/cockpit/static/sw.js
index e0b3570..8f6f931 100644
--- a/src/psyc/cockpit/static/sw.js
+++ b/src/psyc/cockpit/static/sw.js
@@ -5,7 +5,7 @@
// This makes the cockpit installable as a PWA and survives flaky connections,
// without serving stale operational data behind the operator's back.
-const CACHE_VERSION = "psyc-v2";
+const CACHE_VERSION = "psyc-v3";
const STATIC_ASSETS = [
"/static/cockpit.css",
"/static/psyc-tokens.css",
diff --git a/src/psyc/cockpit/templates/base.html b/src/psyc/cockpit/templates/base.html
index 7417974..ffb84a3 100644
--- a/src/psyc/cockpit/templates/base.html
+++ b/src/psyc/cockpit/templates/base.html
@@ -69,6 +69,8 @@
Admin
+ Pulse
+ Federation
{% endif %}
{% if request.session.get('admin_who') %}