Secret

Secrets: named environment values for sandboxes, agents and apps.

openai = noclick.Secret.from_name("openai", values={"OPENAI_API_KEY": "sk-..."}, create_if_missing=True)
with noclick.Sandbox.create(secrets=[openai]) as sb:
    sb.exec("python summarize.py")   # reads os.environ["OPENAI_API_KEY"]

A Connection is an account at a service NoClick calls for you, with tokens it keeps fresh; a Secret is raw values for code you run. Values are write-only: keys names them, and nothing reads one back.

class Secret

Named values, such as API keys, that the code you run reads from its environment: a sandbox's commands, an agent's shell, an app. Each key becomes an environment variable of that name. Values are write-only.

Attributes
idstr
namestr
keyslist[str]

The names it holds, in order; never their values.

user_idstr | None
created_atAny
updated_atAny

When its values last changed.

used_bylist[dict[str, Any]]

Who reads it: kind (agent, a running sandbox or, in invite-only beta, a project), id, name (None for a sandbox) and, for an app, the binding name.

console_urlstr | None

This secret in the developer console (None off the hosted API).

createclassmethodasync

noclick.Secret.create(name, values=None, *, user=..., client=None) -> Secret

Create a secret called name (1-64 letters, digits and . _ -), unique to its user (a taken name raises noclick.Conflict with existing_id), holding values: up to 100 keys, each an environment variable name (letters, digits and _, not starting with a digit; not PATH, HOME or another the process sets, nor one starting LD_, NC_ or NOCLICK_), each value a string of up to 32,768 characters. Without values it starts empty, for someone to fill in from the console.

Parameters
namestrrequired
valuesMapping[str, str] | Nonedefault None
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Secret

The new secret; its keys name what it holds.

Usage
import os

shipping = noclick.Secret.create("shipping", {"SHIPPING_API_KEY": os.environ["SHIPPING_API_KEY"]})
print(shipping.keys)   # ['SHIPPING_API_KEY']

from_nameclassmethod

noclick.Secret.from_name(
    name, *, values=None, create_if_missing=False, user=..., client=None
) -> Secret

A lazy reference to the secret called name. Acting for a user, that user's own secret is found first, then the account's. With create_if_missing=True your code is the definition: the current (or given) user's is created on first use, holding values, and on one that exists the keys in values are set to match (NoClick compares them, so unchanged values change nothing). Keys it doesn't name are kept: remove one with update.

Parameters
namestrrequired
valuesMapping[str, str] | Nonedefault None
create_if_missingbooldefault False
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Secret

A secret that loads (or is created) the first time it's used. Nothing is sent yet.

Usage

Create it from your environment on the first run, and pick up a rotated key on the next:

import os

openai = noclick.Secret.from_name(
    "openai", values={"OPENAI_API_KEY": os.environ["OPENAI_API_KEY"]}, create_if_missing=True,
)
with noclick.Sandbox.create(secrets=[openai]) as sb:
    sb.exec("python summarize.py")   # reads os.environ["OPENAI_API_KEY"]

Or declare it empty and paste the values in the console:

erp = noclick.Secret.from_name("acme-erp", create_if_missing=True, user="acme")
print(erp.console_url)

from_idclassmethod

noclick.Secret.from_id(secret_id, *, client=None, user=...) -> Secret

A lazy reference to a secret by id.

Parameters
secret_idstrrequired
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
Returns
Secret

A secret that loads the first time you read a field. Nothing is sent yet.

Usage
openai = noclick.Secret.from_id("2a7f0c3e-9d1b-4e6a-8c5f-3b0e7d2a9c14")
print(openai.keys)

getclassmethodasync

noclick.Secret.get(secret_id, *, user=..., client=None) -> Secret

Load a secret by id now; an unknown id raises noclick.NotFound.

Parameters
secret_idstrrequired
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Secret

The secret, loaded: its key names, when they last changed, and who uses it. Never its values.

Usage
openai = noclick.Secret.get("2a7f0c3e-9d1b-4e6a-8c5f-3b0e7d2a9c14")
print(openai.name, openai.keys, openai.updated_at)

listclassmethodasync

noclick.Secret.list(
    *, limit=None, user_filter=None, user=..., client=None
) -> list[Secret]

The current user's secrets and the account's (or those user_filter covers).

Parameters
limitint | None

Return at most this many.

(default None)
user_filterAny

Which users a read covers: noclick.ALL_USERS, noclick.NO_USER, a User, or your id for one. It filters without acting as that user.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
list[Secret]

Every matching secret, newest first, fetched page by page, with key names and never values.

Usage
for secret in noclick.Secret.list():
    print(secret.name, secret.keys, secret.user_id)   # user_id is None for the account's

everyone = noclick.Secret.list(user_filter=noclick.ALL_USERS)   # every user's, plus your account's

list_pageclassmethodasync

noclick.Secret.list_page(
    *, cursor=None, limit=None, user_filter=None, user=..., client=None
) -> Page

One page of secrets; pass next_cursor back as cursor.

Parameters
cursorstr | None

The next_cursor of the page before; omit it for the first page.

(default None)
limitint | None

Return at most this many.

(default None)
user_filterAny

Which users a read covers: noclick.ALL_USERS, noclick.NO_USER, a User, or your id for one. It filters without acting as that user.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Page

A page whose data holds the secrets and whose next_cursor is None on the last page.

Usage
page = noclick.Secret.list_page(limit=50)
while True:
    for secret in page.data:
        print(secret.name, secret.keys)
    if page.next_cursor is None:
        break
    page = noclick.Secret.list_page(limit=50, cursor=page.next_cursor)

updateasync

secret.update(values, *, replace=False) -> Secret

Set values. By default they merge: the keys you pass are set, a key set to None is removed, and the rest are kept. replace=True makes the secret hold exactly values. New sandboxes see the change at once, agents from their next turn, and apps at their next deployment.

Parameters
valuesMapping[str, str | None]required
replacebooldefault False
Returns
Secret

This secret, with its new keys and updated_at.

Usage

Rotate one key and keep the rest:

import os

openai = noclick.Secret.from_name("openai")
openai.update({"OPENAI_API_KEY": os.environ["NEW_OPENAI_API_KEY"]})

Remove a key, or replace every value at once:

erp = noclick.Secret.from_name("acme-erp", user="acme")
erp.update({"ERP_TOKEN": None})
erp.update({"ERP_USER": "svc-noclick", "ERP_PASSWORD": "..."}, replace=True)
print(erp.keys)   # ['ERP_PASSWORD', 'ERP_USER']

versionsasync

secret.versions() -> list[Version]

What it held before each change of the last 30 days, newest first: when each was replaced and the keys it held, never the values.

Returns
list[Version]

Each earlier version.

Usage
stripe = noclick.Secret.from_name("stripe")
for version in stripe.versions():
    print(version.replaced_at, version.keys)

restore_versionasync

secret.restore_version(version) -> Secret

Hold version's keys and values again (the values it holds now are kept as a version in turn). Nothing is shown or sent: the earlier values come back sealed.

Parameters
versionAnyrequired
Returns
Secret

This secret, holding them.

Usage

Roll back a key that was rotated by mistake:

stripe = noclick.Secret.from_name("stripe")
stripe.restore_version(stripe.versions()[0])

deleteasync

secret.delete() -> None

Delete the secret and its values, for good (its earlier versions go with it). While an agent lists it or an app binds it (used_by), this raises noclick.Conflict; sandboxes already running keep their environment until they end.

Usage
noclick.Secret.from_name("old-provider").delete()

Version

class Version

A value a secret or setting held before replaced_at. op says what replaced it: changed (a new value) or deleted. A secret's version never shows its value: keys names what a noclick.Secret held and environment is where an app's secret was set; a setting's shows its value.

Attributes
idstr
namestr
opstr
replaced_atAny
environmentstr | None
keyslist[str] | None
valuestr | None
rawdict[str, Any]

The response JSON exactly as the API sent it.