🔶 Launch HN:Coasty (YC S26) – 一个面向计算机使用代理的 API
英文标题:🔶 Launch HN: Coasty (YC S26) – An API for computer-use agents
Coasty(YC S26)推出了 BYOK(自带密钥)功能,允许开发者使用自己的 Anthropic 或 OpenAI API 密钥来驱动计算机使用代理,而不再依赖平台托管模型。密钥可通过 AES-256-GCM 加密存储或请求头传入,永远不会在运行日志或 Webhook 中回显,显著提升安全性与数据主权。平台还支持按代理角色指定不同模型,以便平衡成本与质量,并承诺绝不静默回退到平台密钥。这对注重合规、成本控制与厂商中立的开发团队具有直接价值。
17.6 KB
中文
默认情况下,computer-use harness 中的所有 LLM 调用都在 Coasty 的托管模型上运行。BYOK(自带密钥)翻转了这一模式:选择加入后,整个 harness(包括 worker、grounding、代码代理和 compaction;即每一次 LLM 调用)都将改为在你自己的 Anthropic 或 OpenAI 账户上运行。选择加入始终是显式的,可以按请求或按存储的密钥指定。provider: "managed"(或完全省略 llm)将保持平台默认值,不作任何改变。
有两种方式提供密钥。你可以通过 PUT /v1/llm/keys/{provider} 一次性存储密钥(静态加密,使用 AES-256-GCM;仅回显 sha256 前缀指纹),也可以在每个请求的请求头中传入密钥。请求头中的密钥优先级高于存储的密钥。只发送密钥而未指定提供商将返回 422。
X-LLM-Provider: anthropic X-LLM-Api-Key: sk-ant-your_key_here X-LLM-Model: claude-sonnet-4-6
这些请求头适用于 POST /v1/predict、POST /v1/runs、POST /v1/workflows/{id}/runs、POST /v1/sessions 和 POST /v1/schedules。存储的密钥通过三个端点管理,受 llm_keys 作用域保护(默认授予新创建的生产密钥)。这些端点需要使用生产密钥;沙箱密钥无法读取、覆盖或删除生产凭证存储:
python import os, requests
BASE = "https://coasty.ai/v1" HEADERS = {"X-API-Key": os.environ["COASTY_API_KEY"]}
Store (upsert) your own Anthropic key. Encrypted at rest; never echoed back.
stored = requests.put( f"{BASE}/llm/keys/anthropic", headers=HEADERS, json={"api_key": os.environ["ANTHROPIC_API_KEY"]}, timeout=30, ).json() print(stored) # {"provider": "anthropic", "key_fingerprint": "a1b2c3d4", "stored": true}
List stored keys: provider, fingerprint, timestamps. Never the key itself.
keys = requests.get(f"{BASE}/llm/keys", headers=HEADERS, timeout=30).json() for k in keys["keys"]: print(k["provider"], k["key_fingerprint"])
Delete when you rotate away (404 LLM_KEY_NOT_FOUND when none is stored)
requests.delete(f"{BASE}/llm/keys/anthropic", headers=HEADERS, timeout=30)
在请求体中,相同的端点接受一个 llm 对象,用于选择提供商,并可选择按 harness 角色指定模型。该对象有意不包含 api_key 字段(若尝试添加会返回 422):密钥仅通过请求头或加密存储传递,因此永远不会在运行对象、Webhook 或幂等重放中被回显。
python import os, requests
BASE = "https://coasty.ai/v1" HEADERS = {"X-API-Key": os.environ["COASTY_API_KEY"]}
Start a run on YOUR Anthropic key (stored earlier via PUT /llm/keys/anthropic).
The llm block deliberately has NO api_key field (422 if you try): keys ride
headers or the encrypted store only, never request bodies.
run = requests.post( f"{BASE}/runs", headers=HEADERS, json={ "machine_id": "mch_test_0123456789abcdef", "task": "Open the billing page and download the latest invoice as PDF", "llm": { "provider": "anthropic", # or "openai"; "managed" = platform default "model": "claude-sonnet-4-6", # any model on your account (vision-capable) "compaction_model": "claude-haiku-4-5", # optional per-role override }, }, timeout=30, ).json()
Per-request variant: send the key in headers instead of storing it.
A header key takes precedence over the stored key for this request only.
byok_headers = { **HEADERS, "X-LLM-Provider": "anthropic", "X-LLM-Api-Key": os.environ["ANTHROPIC_API_KEY"], "X-LLM-Model": "claude-sonnet-4-6", }
The run echoes a non-secret llm block; the key itself is never returned.
run = requests.get(f"{BASE}/runs/{run['id']}", headers=HEADERS, timeout=30).json() print(run["llm"]) # {"provider": ..., "model": ..., "key_fingerprint": ..., "key_source": ..., "key_scrubbed": ...}
按角色覆盖可用于在成本与质量之间进行调优:例如,在 compaction 上使用较便宜的模型,而 worker 保持默认。对于运行、工作流和计划,密钥会以加密快照形式存入运行,以确保在副本崩溃恢复后继续使用你的密钥;一旦运行到达终态,密钥即被擦除。GET /v1/runs/{id} 回显一个非机密的 llm 块:{provider, model, key_fingerprint, key_source, key_scrubbed}。计划仅存储非机密偏好;触发时使用当前已存储的密钥。删除存储的密钥后,未来的触发会明确失败并返回 LLM_KEY_NOT_CONFIGURED;它们绝不会静默在平台密钥上运行。
Grounding(像素坐标解析)质量已在平台模型上调优。当在你自己的模型上运行 grounding 时,使用默认设置可获得最佳效果,并可在正式切换到更便宜或不同的模型之前,通过 grounding_model 进行实验。
绝不静默回退:一旦你请求 BYOK,任何代码路径都不能使用 Coasty 的平台 LLM 密钥。来自你的提供商的错误会作为与你密钥相关的问题直接暴露,并提供稳定的错误码:LLM_KEY_NOT_CONFIGURED、LLM_KEY_INVALID、LLM_PROVIDER_AUTH_FAILED、LLM_PROVIDER_RATE_LIMITED、LLM_PROVIDER_QUOTA_EXCEEDED 和 LLM_PROVIDER_ERROR。
计费:使用 BYOK 时,Coasty 对每次调用和每个步骤的平台收费保持不变。模型 Token 直接由你的提供商账户计费。
English / 原文
By default every LLM call in the computer-use harness runs on Coasty's managed models. BYOK (bring your own key) flips that: opt in and the entire harness (the worker, grounding, the code agent, and compaction; every LLM call) runs on your own Anthropic or OpenAI account instead. Opt-in is always explicit, per request or per stored key. provider: "managed" (or omitting llm entirely) keeps the platform default, unchanged.
There are two ways to hand over a key. Store it once with PUT /v1/llm/keys/{provider} (encrypted with AES-256-GCM at rest; only a sha256-prefix fingerprint is ever echoed back), or send it per request in headers. A header key takes precedence over the stored key. Sending a key without a provider returns 422.
X-LLM-Provider: anthropic
X-LLM-Api-Key: sk-ant-your_key_here
X-LLM-Model: claude-sonnet-4-6
The headers work on POST /v1/predict, POST /v1/runs, POST /v1/workflows/{id}/runs, POST /v1/sessions, and POST /v1/schedules. Stored keys are managed through three endpoints, gated by the llm_keys scope (granted to new live keys by default). These endpoints require a live key; sandbox keys cannot read, overwrite, or delete the production credential store:
import os, requests
BASE = "https://coasty.ai/v1"
HEADERS = {"X-API-Key": os.environ["COASTY_API_KEY"]}
# Store (upsert) your own Anthropic key. Encrypted at rest; never echoed back.
stored = requests.put(
f"{BASE}/llm/keys/anthropic",
headers=HEADERS,
json={"api_key": os.environ["ANTHROPIC_API_KEY"]},
timeout=30,
).json()
print(stored) # {"provider": "anthropic", "key_fingerprint": "a1b2c3d4", "stored": true}
# List stored keys: provider, fingerprint, timestamps. Never the key itself.
keys = requests.get(f"{BASE}/llm/keys", headers=HEADERS, timeout=30).json()
for k in keys["keys"]:
print(k["provider"], k["key_fingerprint"])
# Delete when you rotate away (404 LLM_KEY_NOT_FOUND when none is stored)
requests.delete(f"{BASE}/llm/keys/anthropic", headers=HEADERS, timeout=30)
On the request body, the same endpoints accept an llm object that selects the provider and, optionally, a model per harness role. It deliberately has no api_key field (a 422 if you attempt one): keys ride headers or the encrypted store only, so they can never be echoed in run objects, webhooks, or idempotency replays.
import os, requests
BASE = "https://coasty.ai/v1"
HEADERS = {"X-API-Key": os.environ["COASTY_API_KEY"]}
# Start a run on YOUR Anthropic key (stored earlier via PUT /llm/keys/anthropic).
# The llm block deliberately has NO api_key field (422 if you try): keys ride
# headers or the encrypted store only, never request bodies.
run = requests.post(
f"{BASE}/runs",
headers=HEADERS,
json={
"machine_id": "mch_test_0123456789abcdef",
"task": "Open the billing page and download the latest invoice as PDF",
"llm": {
"provider": "anthropic", # or "openai"; "managed" = platform default
"model": "claude-sonnet-4-6", # any model on your account (vision-capable)
"compaction_model": "claude-haiku-4-5", # optional per-role override
},
},
timeout=30,
).json()
# Per-request variant: send the key in headers instead of storing it.
# A header key takes precedence over the stored key for this request only.
byok_headers = {
**HEADERS,
"X-LLM-Provider": "anthropic",
"X-LLM-Api-Key": os.environ["ANTHROPIC_API_KEY"],
"X-LLM-Model": "claude-sonnet-4-6",
}
# The run echoes a non-secret llm block; the key itself is never returned.
run = requests.get(f"{BASE}/runs/{run['id']}", headers=HEADERS, timeout=30).json()
print(run["llm"]) # {"provider": ..., "model": ..., "key_fingerprint": ..., "key_source": ..., "key_scrubbed": ...}
Per-role overrides exist for tuning cost against quality: run compaction on a cheaper model while the worker stays on the default, for example. For runs, workflows, and schedules the key is snapshotted encrypted into the run, so crash-recovery on another replica keeps using your key; it is scrubbed the moment the run reaches a terminal state. GET /v1/runs/{id} echoes a non-secret llm block: {provider, model, key_fingerprint, key_source, key_scrubbed}. Schedules store only the non-secret preference; at fire time the current stored key is used. Delete the stored key and future firings fail loudly with LLM_KEY_NOT_CONFIGURED; they never silently run on platform keys.
Grounding (pixel-coordinate resolution) quality is tuned on the platform model. When running grounding on your own model, expect the best results with the defaults, and use grounding_model to experiment before committing a cheaper or different model to that role.
No silent fallback, ever: once you ask for BYOK, no code path can use Coasty's platform LLM keys. Errors from your provider surface as your key's issue with stable codes: LLM_KEY_NOT_CONFIGURED, LLM_KEY_INVALID, LLM_PROVIDER_AUTH_FAILED, LLM_PROVIDER_RATE_LIMITED, LLM_PROVIDER_QUOTA_EXCEEDED, and LLM_PROVIDER_ERROR.
Billing: Coasty's per-call and per-step platform charges are unchanged with BYOK. The model tokens are billed by your provider account directly.