Skip to content

Sender

A sender is an identity an auth provider can authenticate as. One provider, many users: admin, read-only viewer, a user in another tenant, a service account.

Without senders, testing “does this endpoint reject a non-admin?” means reconfiguring your auth or keeping a second provider around. With them it is a dropdown.

senders:
- id: admin@example.com
label: Staging admin
tenant: acme
secretRef: users-api.admin.PASSWORD
vars:
role: admin
FieldMeaning
idRequired. The identifier the provider authenticates with — a username, an email, a subject id.
labelHuman name shown in the picker.
tenantAn informational label for grouping in the UI. It is not a selection axis on its own.
secretRefThe name of this identity’s keychain entry. Never a value.
varsNon-secret per-sender values, available as {{var}} while the token is being resolved.

A provider with no senders has a single implicit identity, and the script receives sender as null.

They are rows in the provider file, and there are three ways they get there.

Typed in. The manual form takes an id, a label, and — where the provider signs in per sender — a password, which goes to the keychain under a name the file then references.

Fetched from the identity provider. Clerk, Firebase and the rest can list their own users, so the picker shows real accounts rather than asking you to remember an email. A Firebase project configured with a service account can list users this way too. Refreshing re-reads labels and tenants; it never touches a stored secret.

From an OAuth provider’s own endpoints. A generic oauth2 provider populates senders from its userinfo endpoint (the signed-in subject) and, if the server offers one, from an admin or directory endpoint named by sendersUrl.

Not every provider signs in per sender. Clerk uses one shared secret key; an oauth2 sender is an identity the authorization server already knows. The view only asks for a password where the engine would actually refuse without one — password-grant Keycloak and Auth0, Firebase without a service account, Supabase — and a sender missing one is flagged rather than silently failing at send time.

For an awsSigV4 provider a sender is not a person: it is an assume-role target, and the Senders tab is labelled Roles.

The role lives in the sender’s ordinary non-secret varsroleArn (required), plus optional sessionName, externalId, durationSeconds and a region override. No new schema field was added for this, which is the point: “send as the read-only role” is the same dropdown every other provider has.

Selecting one makes the engine call STS AssumeRole with the provider’s base credentials and sign with the returned temporary ones, cached under that sender until they expire. A provider with no roles signs with the base credentials directly.

A request’s auth block names the provider and, optionally, the sender:

auth:
provider: users-api
sender: admin@example.com

Leave sender out and the provider’s default is used. The Auth tab has a picker; the Auth providers view marks one sender as the default with a star.

Tokens are cached per sender, so switching back and forth does not re-authenticate each time.

The selected sender is passed to an auth script, which is how one script serves every identity:

async function authenticate({ secret, sender, env, fetch }) {
const res = await fetch(`${env.baseUrl}/login`, {
method: "POST",
body: JSON.stringify({
user: sender.id,
pass: secret(sender.secretRef),
}),
});
return { token: (await res.json()).token };
}

Authorization bugs are the ones that reach production, because “it worked when I tried it” usually means “it worked as me”. Senders make the other half cheap: send the same endpoint as an admin, as a viewer, and as somebody from a different tenant, and compare the statuses.

An agent can do that too. list_senders returns the identities of one provider or of all of them — id, label, tenant, which is default, and whether the engine would actually be able to use it (with the reason when it could not) — and send_request takes a sender. So “probe this endpoint as each of these three identities and show me the statuses” is one instruction.

Names only, as everywhere else: never a token, a password, or even a secret name.

The tinspec CLI takes --sender for the same reason, which is what lets a pipeline perform the real login rather than replaying a stored token.