Skip to content

HeyAira client profiles

Keep the canonical public client and local test client in separate profiles. The profile is part of the safety boundary; do not reuse a token or a profile between them.

Client-side binding

The three repository smoke clients use the same versioned binding helper from heyaira.client_binding. Each invocation must receive an operator-selected JSON profile with exactly these expectations:

{
  "name": "heyaira-local-test",
  "resource": "http://127.0.0.1:18080/mcp",
  "instance_id": "synthetic-instance-a",
  "project_id": "00000000-0000-4000-8000-000000000001"
}

The profile is loaded before the client makes a mutation. After the read-only server_identity call, the helper compares resource, instance_id, and project_id with the stored expectations. It creates a write_context only after all three match. A missing, malformed, or mismatched profile terminates the client before any write call; the server response is never used to learn or replace the expected values. The profile contains no token or credential.

Canonical public profile

  • profile name: heyaira-public
  • MCP resource: https://mcp.heyaira.eu/p/heyaira/mcp for new connections; a grant issued for the deprecated https://mcp.heyaira.eu/mcp keeps that resource until it reconnects
  • project: 75573220-a2eb-4b99-aab2-71cade5ae03e
  • instance ID: the operator-assigned public value, recorded explicitly before activation; it must not be inferred from a live response
  • use: real task and memory work only after the pinned profile matches

Isolated local-test profile

  • profile name: heyaira-local-test
  • MCP resource: http://127.0.0.1:18080/mcp (or the explicitly assigned test URL)
  • instance ID: a synthetic value unique to that local server
  • project and token: synthetic test data only

Store each profile in its own client configuration scope. The smoke clients accept the profile explicitly with --profile /path/to/profile.json and use the shared helper for every mutation. A task_get not-found result ends the lookup; do not recreate the task or switch profiles.

Per-project resource

A profile's resource is the address the client connects to. Each project has its own, https://<host>/p/<slug>/mcp (migration 024 adds the stable, unique projects.slug). The profile for a per-project connection therefore names that address, and server_identity returns it as identity.resource; a write_context must carry the same value. The deprecated instance-wide /mcp is still a valid resource, and the server still issues new grants for it; a connection on it uses /mcp as its resource.

For terminal-less web connectors, the operator selects the project's MCP address in the connector and completes OAuth at that address. Every OAuth code/access/refresh token stores the resource and project_id it was issued for (migration 002) and the instance_id (migration 007). On an MCP request the server accepts an OAuth token only on the exact address stored with it, so a grant for /p/voyana/mcp is rejected on /p/heyaira/mcp and on /mcp. A static project token (instance-bound by migration 007, no stored resource) is accepted on /mcp and on its own project's address, never on another project's. The server rejects the credential before MCP access when it is presented to another configured instance. This does not require an invented client UI field, and a prompt or an echoed server_identity is still not the security control. Existing OAuth credentials issued before migration 007 are rejected by a configured instance until the user reconnects and receives a newly bound credential.

Activation and rollback

Activation is a staged client-side change: select the intended isolated profile, verify server_identity, run the synthetic smoke test for local-test or the approved public smoke test for canonical public, and only then perform writes. For a backend migration, rollback means redeploying the pre-006 server revision with its matching pre-binding client profile. Migration 006 is additive and is not reversed; existing domain data and audit rows are retained. Merely switching a client profile while leaving the new server live causes visible refusal and is not a server rollback. Returning to a backend without the guard removes the protection and must not be called a safe rollback.