Skip to main content

Overview

Secure[str] lets you pass secrets (passwords, API keys, tokens) to your workflow without exposing them in the sandbox. The real values are held by the orchestrator and injected only at the moment they’re used.
from nen import Secure

Defining Secure Params

Create a Pydantic model where every field uses Secure[str]:
from nen import Secure
from pydantic import BaseModel, Field

class SecureParams(BaseModel):
    password: Secure[str] = Field(min_length=8)
    api_key: Secure[str] = Field(min_length=16)
Every field in SecureParams must use the Secure[str] type. Mixing plain and secure fields is not supported.

Using Secure Params

Add SecureParams as the second parameter to your run() function:
workflow.py
def run(params: Params, secure_params: SecureParams) -> Result:
    computer = Computer()
    computer.type(secure_params.password)  # Typed securely
workflow.py
# Use with Agent for login flows
def run(params: Params, secure_params: SecureParams) -> Result:
    agent = Agent()
    computer = Computer()
    agent.execute("Click the username field")
    computer.type(params.username)
    agent.execute("Click the password field")
    computer.type(secure_params.password)
    computer.press("Return")

How It Works

1

API validates

The API receives the real secret value and validates it as the inner type (e.g., str with min_length=8).
2

Sandbox gets references

The sandbox receives SecureValue("field_name") reference objects — never the actual secret.
3

Secure typing

When you call computer.type(secure_params.password), the reference is sent from the sandbox to the orchestrator, which types the real value into the desktop.

Sending Secrets via API

Requests that include secure_workflow_params must be HMAC-signed. Sending secure_workflow_params with only the x-api-key header returns:
403 { "error": "hmac_required_for_secure_params",
      "message": "The secure_workflow_params field requires HMAC authentication." }
To enable signed requests on an API key, contact your engineering contact — the HMAC signing key is provisioned alongside your API key and delivered out-of-band.

Signing scheme

Every signed request sends three headers:
HeaderValue
x-api-keyYour API key. Still required (API Gateway uses it to look up the signing key.)
x-timestampCurrent time. Accepts Unix seconds, Unix milliseconds, or ISO 8601. Tolerance is ±5 minutes.
x-signaturehmac_sha256(signing_key, canonical_request) as a lowercase hex digest.
The canonical request is four lines joined with \n:
<HTTP_METHOD>
<PATH>
<TIMESTAMP>
<SHA256_HEX(body)>
For a POST /runs, that’s literally POST\n/runs\n<timestamp>\n<body_hash>.

Python example

import hashlib
import hmac
import time

import httpx

SIGNING_KEY = "your_hmac_signing_key"   # provisioned alongside your API key
API_KEY     = "your_api_key"

body = (
    '{"workflow_id":"login-workflow",'
    '"workflow_params":{"username":"jane@example.com"},'
    '"secure_workflow_params":{"password":"s3cret-passw0rd"}}'
)
timestamp = str(int(time.time()))
body_hash = hashlib.sha256(body.encode()).hexdigest()
canonical = f"POST\n/runs\n{timestamp}\n{body_hash}"
signature = hmac.new(
    SIGNING_KEY.encode(), canonical.encode(), hashlib.sha256
).hexdigest()

httpx.post(
    "https://api.getnen.ai/runs",
    content=body,  # must be the exact bytes you hashed
    headers={
        "x-api-key": API_KEY,
        "x-timestamp": timestamp,
        "x-signature": signature,
        "Content-Type": "application/json",
    },
)
Send exactly the same body bytes you hashed. Any reformatting, whitespace change, or JSON key reordering between signing and sending will produce a different hash on the server and the request will be rejected.
Secrets are validated against your SecureParams field constraints before the workflow starts. A password shorter than 8 characters will fail validation immediately, regardless of whether the HMAC check passes.

SecureValue

SecureValue is the reference object that represents a secret inside the sandbox. You never construct these directly — they are injected by the sandbox harness when the workflow starts.

Properties

PropertyTypeDescription
identifierstrField name from SecureParams

Supported Operations

SecureValue objects can only be passed to computer.type():
computer.type(secure_params.password)   # ✅ Works
print(secure_params.password)           # ❌ Prints reference, not value
str(secure_params.password)             # ❌ Returns reference string

Key Guarantees

PropertyDescription
Never in the sandboxReal values exist only in the orchestrator’s memory
Validated before useField constraints are enforced on the real value at the API layer
Type-safeSecure[str] is a proper Python type — your editor will flag misuse
Audit-safeSecret values never appear in logs, error traces, or sandbox output

Adding Secrets via Dashboard

  1. Navigate to your deployment or workflow
  2. Click Secrets in the sidebar
  3. Click Add Secret
  4. Enter the name and value
  5. Click Save
Use descriptive names like EHR_PASSWORD or API_TOKEN for easy identification.

Scope Levels

ScopeDescriptionUse Case
DeploymentAvailable to all workflows on a deploymentShared credentials
WorkflowAvailable only to a specific workflowWorkflow-specific keys
OrganizationAvailable across all deploymentsGlobal API keys

Security

Encryption

  • At rest: AES-256 encryption
  • In transit: TLS 1.2+
  • Key management: AWS KMS or equivalent

Access Control

  • Secrets are scoped to deployments/workflows
  • API key required for access
  • Audit logging for all secret operations

Automatic Redaction

Secret values are automatically redacted from:
  • Workflow logs
  • Error messages
  • API responses
  • Video recordings