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/mcpfor new connections; a grant issued for the deprecatedhttps://mcp.heyaira.eu/mcpkeeps 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.