# Security model

> Why Helix code can call authenticated APIs safely: credentials stay on the platform, injected server-side, with every call audit-logged.

Helix project code never contains, receives, or logs API credentials. Functions name a credential by alias; the Helix proxy resolves the alias, injects the secret server-side, forwards the request, and writes an audit log entry. This page walks through the mechanics for the engineer or IT reviewer asking why that is safe.

## Credentials never enter project code

A project's repository stores only a mapping from readable aliases to credential UUIDs:

```typescript
// helix.config.ts
import { defineConfig } from '@trayai/helix-sdk';

export default defineConfig({
  workspaceId: 'f8e7d6c5-4b3a-2190-8765-abcdef012345',
  projectId: 'a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d',
  authentications: {
    salesforce_prod: '1a0fbf5c-2c9e-4aa1-ada3-ccbba65019ab',
  },
});
```

The UUID names a credential record on Helix's servers, created and managed in the workspace (see [connected services](/documentation/guides/connected-services/)). Nothing in the repository, the environment, or the function runtime holds a token. When code makes an authenticated call:

```typescript
// functions/api/v1/contacts.get.ts
import { defineFunction } from '@trayai/helix-sdk';

export default defineFunction(async (ctx) => {
  const res = await ctx.http
    .authed('salesforce_prod')
    .get('https://acme.my.salesforce.com/services/data/v59.0/query', {
      query: { q: 'SELECT Id, Name FROM Contact' },
    });

  return res.body;
});
```

the SDK routes the request through the Helix proxy. The function's process never connects to Salesforce directly and never sees the token.

## What the proxy does with every call

1. **Validates the caller** and resolves which credential the alias maps to.
2. **Injects credentials** using the signing strategy for that credential type (see below), refreshing an OAuth token first if it has expired.
3. **Forwards** the signed request to the target API and streams the response back to your function.
4. **Retries once on auth failure.** If the target returns a 401 and the credential is OAuth-based, the proxy refreshes the token and retries once.
5. **Logs.** The full request and response are recorded with credentials redacted, tagged with project ID, execution ID, and auth alias, and visible in the dashboard at `https://app.helix.tray.ai`.

### Signing strategies

The credential type determines how credentials are injected. Code stays identical when a credential rotates or when two services authenticate differently. Helix injects credentials in these ways today:

| Strategy | How credentials are injected | Example services |
|---|---|---|
| Bearer token (OAuth 2.0) | `Authorization: Bearer <token>` header | Salesforce, Slack, GitHub, most OAuth 2.0 APIs |
| API key (header) | A named header such as `x-api-key` | OpenAI, Stripe, SendGrid |
| Bearer token, basic auth, or API key in a header or query string | Added to the request as you configure it in a generic service | Any API not in the supported services list. See [connect an API that isn't listed](/documentation/guides/connect-any-api/) |
| OAuth 1.0a, HMAC, or AWS Signature v4 | The request is signed with the secrets you enter in a generic service | Any API not in the supported services list that signs requests. See [signed requests](/documentation/guides/connect-any-api/#signed-requests) |

## What code can see

`ctx.auth('alias')` returns only fields the credential record classifies as non-sensitive, such as instance URLs and subdomains. Tokens, API keys, and secrets are never returned:

```typescript
// functions/api/v1/sync.post.ts
import { defineFunction } from '@trayai/helix-sdk';

export default defineFunction(async (ctx) => {
  const vars = await ctx.auth('salesforce_prod');
  // { instance_url: 'https://mycompany.my.salesforce.com', subdomain: 'mycompany' }

  ctx.log.info('Syncing from org', { subdomain: vars.subdomain });
  return { started: true };
});
```

The same boundary holds in the dashboard: workspace readers can see which credentials a project uses, never their values.

## Execution identity

Each execution is identified by the platform so logs and audit trails can attribute work to a project, workspace, and caller. That identity is established server-side; function code does not supply or control it.

## Visibility for admins

Org admins can see projects, authentications in use, deployments, the activity trail, and per-request execution detail in the [Admin console](/documentation/governance/admin-console/) and the [dashboard](/documentation/governance/dashboard/). See [governance](/documentation/governance/).

## Encryption

- **At rest:** AES-256 for platform-stored data, including the project key-value store.
- **In transit:** TLS 1.3 for all network traffic.
- **Credentials:** encrypted by the platform and never exposed to function code.

## The same guarantees in local development

`helix dev` changes none of this. `ctx.http.authed()` calls go to the remote auth service even locally, so credentials are never downloaded to a developer's machine.

---

Canonical: https://helix.tray.ai/documentation/concepts/security-model/
Any link on this page is available as markdown by appending .md to its URL.
Full corpus: https://helix.tray.ai/documentation/llms-full.txt