Skip to main content

Authentication

If we already know the user may use a tool (SAP, HubSpot, Google, …), we ask HashiCorp Vault for credentials, Vault returns them, and our code connects to the tool and gathers the data. Vault never returns HubSpot/SAP business records. The agent never sees the secrets.

Nono and pi-authentication sit beside that loop. They do not fetch Vault secrets.

Longer write-up: HashiCorp Vault proposal. Sandbox detail: Agent sandboxing.

Implemented as this Vault path (VAULT_OAUTH_BACKEND=hashicorp). Google and HubSpot Connect on Tools remain as a manual path; chat JIT is the primary path via needs_connection.


Three kinds of “auth” (do not mix them)​

NameProvesHeld byUsed for
User loginThis browser is AliceBrowser cookieOpening the app, sending a chat turn
Pi-authentication (PI_USER_TOKEN)This sandbox is Alice’s box, not Bob’sLandlocked piDjango internal APIs that must run as Alice (Memory, reports, the Enterprise Database)
Vault vendor secretsHow to log into SAP / HubSpot / GoogleDjango onlyOur code connecting to the vendor
NonoNot identitySupervisor around piLock the box (Landlock) and, today, inject a phantom vendor key so pi never sees the real token

PI_USER_TOKEN is signed with PI_USER_TOKEN_SECRET (see chat/pi_user_token.py, chat/pi_sandbox_auth.py). It names one user id. The sandbox never gets MCP_INTERNAL_SECRET (that would let it act as any user).


Role of nono​

Nono is the in-box supervisor, not a login system and not a Vault client.

Docker container
nono (reads session.json, Landlock, optional vendor proxy)
└── pi (the CLI / agent — untrusted)
Nono doesNono does not
Wrap pi (nono -s run --profile … -- pi …)Authenticate the human
Landlock: which files, env vars, and hosts pi may touchCall HashiCorp Vault
Today: proxy Gmail/HubSpot — child sends phantom HUBSPOT_API_KEY, nono injects the real BearerHold Vault AppRole, client_secret, SAP password
Enforce “this session may talk to HubSpot paths X Y Z”Decide “is Alice allowed SAP?” (that is Django)

In the target Vault loop, SAP (and ideally HubSpot/Google too) are called by Django. Nono then only sandboxes pi. Pi asks Django for data; Django already has the vendor result. Pi does not present Vault credentials to SAP.


Role of pi-authentication​

Pi-authentication is how the box proves which user it is, once Django has already logged Alice in and started her sandbox.

Pi-authentication isPi-authentication is not
HMAC token: user_id + expiry + signatureA Vault token
Scoped to one userShared org secret (MCP_INTERNAL_SECRET)
How Django's internal APIs know which AliceHow we log into SAP
Safe to put in the sandboxA license to read Bob’s HubSpot

The vendor-data loop (Vault)​

User allowed? → Django asks Vault for credentials → Vault returns secrets
→ our code connects to the vendor → data comes back to the chat
StepWho
1. LoginDjango session
2. User asks for dataChat → Django starts/uses the pi sandbox
3. Access checkDjango policy. If no, stop. No Vault.
4. CredentialsDjango ← Vault
5. ConnectDjango → SAP / HubSpot / Google
6. DataVendor → Django → chat. Pi may display the data. Nono never saw the secret.

Sequence (all three together)​

Pi can still call Django's /api/internal/… routes with PI_USER_TOKEN in the same session. That path never uses Vault. Gmail, Sheets, and HubSpot do not use it either: those go through nono's phantom-key proxy. There is no MCP server in the sandbox.


Decision flow​


What Vault returns vs what the vendor returns​

  • Vault: passwords, client id/secret, tokens, certs → Django.
  • Vendor: records → Django.
  • Pi-authentication: “this box is Alice” → Django internal APIs.
  • Nono: containment (+ today’s Gmail/HubSpot phantom-key proxy).
  • User / model: business data only.

What never happens​

  • Calling Vault before we know the user is allowed
  • Vault queried for “SAP customers”
  • Vault secrets in pi, in chat, or in nono’s child env
  • Nono or pi calling Vault
  • PI_USER_TOKEN used as a SAP/HubSpot password
  • MCP_INTERNAL_SECRET given to the sandbox

FAQ​

“The credentials are shared inside the pi session.”
Vault credentials stay in Django. Pi may see business data and PI_USER_TOKEN (who the box is). Those are not SAP passwords.

Today’s Gmail/HubSpot path is weaker: a short-lived access token sits on the nono supervisor in the same container; Landlocked pi only has a phantom *_API_KEY. SAP should not copy that.

“The pi CLI is treated as a separate service.”
Pi is not a microservice and not a Vault client. It is the process nono starts in the sandbox. Separate services: Django, Vault, Postgres. Pi-authentication only answers “which user does this box represent?”