AgentNava · Build

Where a key may be sent

Each key goes only to the hosts it is pinned to, and only over https. A tool cannot send it anywhere else, whoever wrote the tool.

The rule

A tool uses a key by writing {{secrets.NAME}} in its address, headers or body. Every call is checked after the placeholders are filled, just before it goes out:

The agent
Calls a tool
It picks the tool and its parameters. It never sees the key.
↓
AgentNava
Fills in the key, then checks where the call goes
The address must be https, and its host must be in the list of every key the call uses.
↓both true
Your API
Gets the call
Otherwise the call is not made, and the tool answers with the reason in plain words.

Redirects are checked the same way. A redirect is followed only to a listed host, five hops at most.

How a key gets its hosts

You rarely set the list by hand. The first time you save a tool that uses a key, the key is pinned to every host it is already sent to in the workspace:

  • the hosts in this save,
  • this agent's draft and every stored version of it, so a conversation still on an older version keeps working,
  • and, for a workspace key, every other agent that sends it, archived agents included.

So the first pin never cuts off a tool that was working. You typed those hosts, so saving is the consent: there is no extra dialog. The pin is written only after the save succeeds, and two first saves at the same moment keep both their hosts.

MomentWhat the key may reach
Not pinned yetOnly the host its tool writes out in full
First save of a tool that uses itPinned to every host already in use
Any later saveOnly the pinned hosts. A save that sends it to a new host is refused
You add a host on purposeThe list you set with setAllowedHosts

Only a destination the save adds is checked. An unrelated edit to an agent is never blocked by a key it already uses.

Write the host out in full

The host decides where a key goes, so a tool that sends a key must start its address with the scheme and a written-out host. A placeholder cannot build them.

Address of a tool that sends a key
https://api.helpdesk.example.com/v2/tickets/{{params.id}}Saved
https://{{params.region}}.helpdesk.example.com/v2/ticketsRefused: a placeholder builds the host
{{secrets.BASE_URL}}/v2/ticketsRefused: a placeholder builds the scheme and host
http://api.helpdesk.example.com/v2/ticketsSaved, but never called: a key goes only over https

A tool that sends no key may keep placeholders anywhere, as before. Only a new or changed tool is checked when you save. An older tool you have not touched still saves, but its calls that use a key are refused until its address is written out in full.

Addresses are saved in one spelling: trimmed, the scheme and host in lower case, and a default port (:443 for https) dropped. So HTTPS://API.Example.com:443/x and https://api.example.com/x are the same host.

What gets refused, and what it says

Every refusal names the fix. The messages are exactly these:

When, and where you see itThe message
A save sends a pinned key to another host.
The save fails with 400, code secret_host_not_allowed. The SDK throws SecretHostNotAllowed
HELPDESK_TOKEN is only sent to api.acme.example. Use a separate secret for api.other.example.
A save gives a tool that sends a key a placeholder host.
The save fails with 400
Type the host in the URL yourself. The agent cannot choose where a secret goes.
A call goes to a host not in the list.
The tool's answer to the agent
The key HELPDESK_TOKEN is only sent to api.acme.example, so the call to api.other.example was not made.
A call uses a key that has no host it may go to.
The tool's answer
The key HELPDESK_TOKEN is not set up to be sent to any host, so the call to api.other.example was not made.
A call's key has no value (never given, or the attached secret was deleted).
The tool's answer, which names no key
This tool is not available right now. Tell the person, in plain words, that you cannot do this at the moment, without a reason or technical detail, and carry on with what you can do.
A pushed sign-in has expired (a value your app pushed for the conversation, past its expiry).
The tool's answer, which names no key
The sign-in this tool uses has expired, so the call was not made. Tell the person, in plain words, that their sign-in expired and the app needs to sign them in again.
A call that uses a key is not https.
The tool's answer
This tool sends a key, and a key is only sent over https, so the call was not made.
A secret value is shorter than 8 characters.
Creating or replacing the secret fails with 400, code secret_value_too_short
value must be at least 8 characters: a shorter one cannot be hidden when an API echoes it back
An import cannot find a tool's base address.
In draft.warnings, and in Import's skipped list in the app
Skipped GET {{baseUrl}}/tickets: This collection does not include its base address (baseUrl). Add it to the collection's variables in Postman, then import again.

A refused call is an ordinary tool answer, so the agent explains it to the person and the conversation goes on. The key's value never appears in it, and the turn's tool event for that call ends with ok: false.

why 8 characters

We hide a key's value if an API ever echoes it back in a reply, including its usual re-spellings: URL-encoded, base64, and without a Bearer prefix. A value shorter than 8 characters could not be hidden without mangling ordinary text, so it is not accepted as a secret.

Send a key to another host

Usually, use a separate key. One key per service keeps each key's reach small, and a leak of one does not expose the other. Create the second key under its own name and use that name in the new tool.

When one key really serves two hosts, add the host on purpose from the SDK. Saving a tool never adds one.

// pin it when you create it
const key = await ws.secrets.create({
  name: 'HELPDESK_TOKEN',
  value: process.env.HELPDESK_TOKEN,
  allowedHosts: ['api.helpdesk.example.com'],
});

// later: replace the list (it is not a merge, so name every host)
await key.setAllowedHosts(['api.helpdesk.example.com', 'uploads.helpdesk.example.com']);
key.allowedHosts;   // ['api.helpdesk.example.com', 'uploads.helpdesk.example.com']

// [] unpins it: the next tool saved with it pins its own host again
await key.setAllowedHosts([]);
An entryMatches
api.example.comExactly that host
*.example.comOne more label: api.example.com, not a.b.example.com and not example.com
api.example.com:8443That host on that port

Write the host alone, with no https:// and no path. At most 20 hosts per key. A wrong entry is refused with the reason, for example: allowedHosts: "https://api.example.com" is not a host. Write the host alone, like "api.example.com", with no https:// and no path.

Handling the refusal in code

import { SecretHostNotAllowed } from '@cerebro-labs/agentnava-sdk';

try {
  await agent.update({ httpTools });
} catch (err) {
  if (err instanceof SecretHostNotAllowed) {
    err.code;          // 'secret_host_not_allowed'
    err.secret;        // 'HELPDESK_TOKEN'
    err.host;          // 'api.other.example.com'
    err.allowedHosts;  // ['api.helpdesk.example.com']
  }
  throw err;
}

SecretHostNotAllowed is an AgentNavaError, so code that branches on err.code keeps working.

An end user's token

The person's token isIt may be sent to
Stored for them and attached, with allowedHostsOnly the hosts in that list. It is read from the stored secret at every call, so a list you change applies to running conversations too
Stored for them and attached, with no listOnly the host written out in the tool that names it
Pushed with the conversation (secrets on start, ask or attach)Only the host written out in the tool that names it
const key = await ws.endUser(user.id).secrets.create({
  name: 'HELPDESK_TOKEN',
  value: token,
  allowedHosts: ['api.helpdesk.example.com'],
});

See Credentials for your users.

In the app

  • Where a key may go is fixed the first time a tool that uses it is saved. The app cannot change it.
  • To send a key to another host, add a separate key on the Secrets tab and use it in the new tool.
  • The Agent Builder never changes where a key goes. If a change it makes is refused because a key is only sent to another host, it tells you to add a separate key.
  • A token you type into an HTTP tool must be at least 8 characters.