Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 51 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,10 @@ cannot switch projects.
| `kernel vaults list` | `--limit 1..100` (default 20), `--offset`; JSON includes `vaults` and optional `next_offset` |
| `kernel vaults get <vault>` | Get by ID or name |
| `kernel vaults delete <vault>` | Invalidate the vault and all its items; `--yes` skips confirmation |
| `kernel vaults wallets create <vault> <key> --provider link\|agentcard --spec '<json>'` | Connect/enroll a wallet using its provider's spec; `--open` opens a returned HTTPS action URL |
| `kernel vaults wallets create <vault> <key> --provider link\|agentcard\|kernel --spec '<json>'` | Connect/enroll a wallet using its provider's spec; `--open` opens a returned HTTPS action URL |
| `kernel vaults wallets get <vault> <key>` | Observe wallet state and its hosted action; `--wait 0..60`, `--open` |
| `kernel vaults wallets payment-methods <vault> <key>` | Fetch advertised live payment methods; JSON is the item with `expanded.payment_methods` |
| `kernel vaults cards create <vault> <key> --provider link\|agentcard --spec '<json>'` | Create a card request; never implicitly authorize Link |
| `kernel vaults cards create <vault> <key> --provider link\|agentcard\|kernel --spec '<json>'` | Create a card request without authorizing it |
| `kernel vaults cards update <vault> <key> --provider link\|agentcard --spec '<json>'` | Update a card spec; pending issuance preserves omitted optional fields, and the API enforces state/provider constraints |
| `kernel vaults items list <vault>` | List item keys, types, providers, status, and required actions |
| `kernel vaults items get <vault> <key>` | Inspect state/actions/returned AgentCard aliases and copyable operation commands; `--wait 0..60`, `--expand payment_methods`, `--open` |
Expand All @@ -414,7 +415,8 @@ its generated item ID. Names and keys use letters, digits, dots, underscores, an
JSON preserves field presence and API-returned AgentCard aliases, while omitting unknown fields,
opaque metadata, and unrecognized event data. Human output labels aliases as non-secret
checkout values and distinguishes card readiness from checkout authorization/payment outcomes.
Link cards do not expose aliases or support egress substitution; browser checkout uses only `fill`.
Link and Kernel cards do not expose aliases or support egress substitution; browser checkout
uses only advertised `fill`.
Action and approval URLs print in full on separate lines, without table truncation.
Most API failures use the CLI's standard error formatter. Wallet creation and provider config
commands withhold response/transport details to prevent credential echoes; HTTP status remains visible.
Expand All @@ -425,23 +427,29 @@ Other API errors still return a nonzero exit status.
**Provider specifications:** wallet creation and card creation/update require `--provider`
and `--spec '<json>'`. Supply only the spec object, not a `{type, spec}` envelope. The command
sets the item type and injects `provider`; if JSON also contains `provider`, it must match.
Other values are forwarded unchanged, including optional fields, without defaults or normalization.
The API validates the provider-specific schema. Each command's `--help` includes its raw
Other non-secret values are forwarded unchanged, including optional fields, without defaults
or normalization. The API validates the provider-specific schema. Each command's `--help` includes its raw
TypeScript-style types, which must stay in sync with the [API spec](https://api.onkernel.com/spec.yaml).

- **Kernel wallet:** use `{}`; no provider configuration or token file is accepted. The hosted
`card_enrollment` action collects card details from the cardholder, never from the CLI.
- **Link wallet:** supply `authorization: {method: "oauth", client: {type: "kernel_managed"}}`.
- **AgentCard wallet:** use `{}` to enroll, or supply `user_id` for a user enrolled in the same organization and provider configuration.
- **Kernel card:** supply `wallet`, `amount` (minor units, at most 50000), `currency`,
`merchant_name`, and HTTPS `merchant_url`. Supply two-letter `merchant_country` for Visa.
Kernel card updates are unsupported.
- **Link card:** include the required fields shown in help. Optional `line_items`, `totals`,
`metadata`, and `expires_at` are supported through JSON.
- **AgentCard card:** uses `merchant`, not Link's `merchant_name`. Its optional `card_id` selects
a vaulted card; otherwise the cardholder selects one at approval.

`cards update` replaces the spec for requested cards. Pending issuance updates preserve omitted
optional fields; explicit empty lists clear them. Wallet/provider bindings and unsupported fields
`cards update` replaces the spec for supported providers' requested cards; Kernel cards
cannot be updated. Pending issuance updates preserve omitted optional fields; explicit empty lists clear them. Wallet/provider bindings and unsupported fields
cannot change after authorization starts. Checkout cards can be edited between authorizations.
An uncertain update enters `recovery_required` and must not be retried. Identical card creation
returns its existing state without polling, reauthorizing, or resetting recovery. Permitted
checkout domains remain provider-assigned. Neither command submits a merchant payment.
returns its existing state without polling, reauthorizing, or resetting recovery. Link and
AgentCard checkout domains remain provider-assigned; Kernel fill is locked to the exact origin
of `merchant_url`. Neither command submits a merchant payment.

#### Provider configurations and imported grants

Expand Down Expand Up @@ -507,6 +515,40 @@ Do not retry, delete, or replace the original operation. Reconcile with the prov
there is no reset or caller-asserted reconciliation endpoint. Unresolved child cards can block
wallet and vault deletion. Time passing or deletion is not evidence of non-execution.

#### Kernel-managed card checkout

```bash
kernel vaults create --name checkout
kernel vaults wallets create checkout cardholder --provider kernel --spec '{}' --open
kernel vaults wallets get checkout cardholder --wait 60
kernel vaults wallets payment-methods checkout cardholder
```

Share the returned `card_enrollment` URL with the cardholder. A connected wallet may still
report `single_use_card.eligible: false` with a `network_token_*` reason while network token
enrollment is pending or unsupported. Inspect the expansion before requesting a card; this
wallet uses its enrolled card, not a `payment_method_id` in the card spec.

```bash
kernel vaults cards create checkout order-1 --provider kernel --spec '{
"wallet":"cardholder", "amount":1200, "currency":"usd",
"merchant_name":"Example Shop", "merchant_url":"https://shop.example/checkout",
"merchant_country":"US"
}'
kernel vaults items get checkout order-1
kernel vaults items invoke checkout order-1 authorize --open
kernel vaults items get checkout order-1 --wait 60
```

Only invoke `authorize` when advertised. Visa may return a `spend_approval` URL for the
cardholder; Mastercard may become ready without one. An unresolved `recovery_required` item
must not be retried, deleted, or replaced. When ready, create a browser with `--vault checkout`
and invoke the advertised `fill` operation with the browser ID, an HTTPS `page_url` on the
exact origin of `merchant_url`, and the checkout field selectors (see `items invoke --help`).
Fill types the one-time network token and code but does not submit the merchant form.
Submit before `expires_at`; neither ready nor fill proves that the merchant charged the card.
Never put PAN, CVC, or enrollment credentials in CLI arguments or logs.

#### Link checkout preparation

Link OAuth, user approval, and provider-issued single-use card issuance precede browser checkout.
Expand Down
62 changes: 45 additions & 17 deletions cmd/vaults_commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,22 @@ Otherwise, the API resolves the project from your credentials and its defaults.
Vault names, item keys, and project ownership are immutable.

1. Create/select a vault, then create a provider wallet and follow its returned action.
2. For Link, list wallet payment methods and select an ID explicitly.
For Kernel, share the hosted card_enrollment URL; card details stay on that page.
2. Inspect wallets payment-methods for eligibility and reasons before creating a card.
For Link, select a payment method ID explicitly.
3. Create a card request with --provider and --spec JSON.
4. Inspect items get, then use items invoke <vault> <key> <operation> only when advertised.
Follow the operation description and any returned provider action.
5. Attach the vault with browsers create --vault <id-or-name>; attachment is required for fill.
Ready Link cards use only advertised fill with --params for browser checkout.
Link cards do not expose aliases or support egress substitution.
Ready Link and Kernel cards use only advertised fill with --params for browser checkout.
Neither exposes aliases or supports egress substitution. Kernel fill is locked to
merchant_url's origin; submit the merchant checkout before the card expires.
AgentCard-only checkout aliases support egress substitution with checkout hold,
approval, and replay; they are not a fallback after fill.
Inspect items get/events for payment outcomes.
Inspect items get/events for vault outcomes; Kernel does not observe merchant charges.

Permitted checkout domains are provider-assigned and displayed when returned;
Permitted checkout domains are provider-assigned and displayed when returned; Kernel
card fill instead uses the exact origin of merchant_url.
there is no domain-setting API.
Never supply card data, OAuth codes, ciphertext, or secrets in shell arguments.
Use vault-provider-configs for client credentials and wallets create --tokens-file
Expand Down Expand Up @@ -175,8 +179,9 @@ exp_year (YYYY), billing_name, billing_line1, billing_line2, billing_city,
billing_state, billing_postal_code, billing_country. expiration requires format MM/YY
or MM/YYYY. Optional timeout_ms is 1-30000 (default 10000).
The API searches the page and descendant frames, including payment iframes.
Fill is available for credential items and ready Link cards when advertised, not AgentCard.
Link cards do not expose aliases or support egress substitution.
Fill is available for credential items and ready Link or Kernel cards when advertised, not AgentCard.
Link and Kernel cards do not expose aliases or support egress substitution. Kernel card fill
is locked to merchant_url's origin and its one-time code expires; submit checkout before expiry.
Fill writes real values into the browser; unrestricted browser/CDP access can read them.
Fill never explicitly submits forms or clicks buttons, but input/change events may trigger site behavior.
completed means fields were filled, not website acceptance, login, or payment success.
Expand Down Expand Up @@ -237,6 +242,7 @@ JSON
kernel vaults items invoke user-vault login webmcp_invoke --spec-file - <<'JSON'
{"browser_id":"<browser-id>","tool_ref":"<tool-ref>","page_url":"https://example.com/login","input":{"email":null,"password":null},"bindings":[{"field":"email","input_path":"/email"},{"field":"password","input_path":"/password"}]}
JSON
kernel vaults items invoke checkout order-1 authorize --open
kernel vaults items invoke user-vault github 1pw_create_access_request --params '{"browser_id":"<browser-id>","reason":"Sign in to GitHub"}'
kernel vaults items invoke user-vault github 1pw_access_request_status --params '{"browser_id":"<browser-id>","timeout_seconds":60}'
kernel vaults items invoke user-vault github 1pw_fill --params '{"browser_id":"<browser-id>","page_url":"https://github.com/login"}'
Expand Down Expand Up @@ -273,7 +279,7 @@ JSON
items.AddCommand(itemList, itemGet, itemEvents, invoke, newVaultWebMCPCommand(), newVaultDeleteCommand(true))

wallets := &cobra.Command{Use: "wallets", Short: "Connect provider wallets and inspect funding methods"}
walletCreate := &cobra.Command{Use: "create <vault> <key> --provider <link|agentcard> --spec '<json>'", Short: "Create a wallet and display its connection or enrollment action", Args: cobra.ExactArgs(2), PreRunE: vaultPreRun,
walletCreate := &cobra.Command{Use: "create <vault> <key> --provider <link|agentcard|kernel> --spec '<json>'", Short: "Create a wallet and display its connection or enrollment action", Args: cobra.ExactArgs(2), PreRunE: vaultPreRun,
Long: "Create a wallet at an immutable key and follow the returned provider action.\n" + vaultSpecHelp + vaultWalletSpecHelp,
Example: ` kernel vaults wallets create checkout wallet-1 \
--provider link --spec '{
Expand All @@ -284,7 +290,9 @@ JSON
}' --open

kernel vaults wallets create checkout wallet-1 \
--provider agentcard --spec '{}'`,
--provider agentcard --spec '{}'

kernel vaults wallets create checkout cardholder --provider kernel --spec '{}' --open`,
RunE: func(cmd *cobra.Command, args []string) error {
spec, err := vaultWalletSpecFromFlags(cmd)
if err != nil {
Expand All @@ -307,7 +315,17 @@ JSON
return getVaultsHandler(cmd).GetItem(cmd.Context(), args[0], args[1], 0, []string{"payment_methods"}, resolveProjectSelection(project), vaultOutput(cmd), false)
}}
addVaultJSONOutputFlag(methods)
wallets.AddCommand(walletCreate, methods)
walletGet := &cobra.Command{Use: "get <vault> <key>", Short: "Get a wallet's state and hosted enrollment action", Args: cobra.ExactArgs(2), PreRunE: vaultPreRun,
RunE: func(cmd *cobra.Command, args []string) error {
project, _ := cmd.Flags().GetString("project")
wait, _ := cmd.Flags().GetInt64("wait")
open, _ := cmd.Flags().GetBool("open")
return getVaultsHandler(cmd).GetItem(cmd.Context(), args[0], args[1], wait, nil, resolveProjectSelection(project), vaultOutput(cmd), open)
}}
walletGet.Flags().Int64("wait", 0, "Hold while pending for up to this many seconds (0-60); observe only")
walletGet.Flags().Bool("open", false, "Open a returned HTTPS enrollment URL")
addVaultJSONOutputFlag(walletGet)
wallets.AddCommand(walletCreate, walletGet, methods)

cards := &cobra.Command{Use: "cards", Short: "Configure card requests"}
cards.AddCommand(newVaultCardCommand(false), newVaultCardCommand(true))
Expand Down Expand Up @@ -338,10 +356,14 @@ func newVaultCardCommand(update bool) *cobra.Command {
if update {
use, short = "update", "Update a card spec when the API permits configuration"
}
cmd := &cobra.Command{Use: use + " <vault> <key> --provider <link|agentcard> --spec '<json>'", Short: short, Args: cobra.ExactArgs(2), PreRunE: vaultPreRun,
Long: short + `. Neither create nor update authorizes a Link card.
Requested cards accept a replacement spec. Pending issuance updates preserve omitted
optional fields; explicit empty lists clear them. The API restricts fields after
providers := "link|agentcard|kernel"
if update {
providers = "link|agentcard"
}
cmd := &cobra.Command{Use: use + " <vault> <key> --provider <" + providers + "> --spec '<json>'", Short: short, Args: cobra.ExactArgs(2), PreRunE: vaultPreRun,
Long: short + `. Neither create nor update authorizes a card.
Kernel cards cannot be updated. Other requested cards accept a replacement spec.
Pending issuance updates preserve omitted optional fields; explicit empty lists clear them. The API restricts fields after
authorization starts; wallet/provider bindings cannot change. An uncertain update
enters recovery_required and must not be retried. Checkout cards can be edited
between authorizations. Identical creates return existing state without resetting it.
Expand All @@ -360,6 +382,9 @@ A recovery item that permits abandonment must be deleted after explicit user con
if err != nil {
return err
}
if update && string(spec["provider"]) == `"kernel"` {
return fmt.Errorf("Kernel card updates are not supported; create a new card item for a new purchase")
}
return getVaultsHandler(cmd).SaveCard(cmd.Context(), args[0], args[1], param.Override[kernel.CardVaultItemSpecUnionParam](spec), update, vaultOutput(cmd))
}}
addVaultSpecFlags(cmd)
Expand All @@ -368,16 +393,16 @@ A recovery item that permits abandonment must be deleted after explicit user con
}

func addVaultSpecFlags(cmd *cobra.Command) {
cmd.Flags().String("provider", "", "Provider: link or agentcard (required)")
cmd.Flags().String("provider", "", "Provider: link, agentcard, or kernel (required)")
cmd.Flags().String("spec", "", "Raw JSON specification object (required); see types and examples above")
_ = cmd.MarkFlagRequired("provider")
_ = cmd.MarkFlagRequired("spec")
}

func vaultSpecFromFlags(cmd *cobra.Command) (map[string]json.RawMessage, error) {
provider, _ := cmd.Flags().GetString("provider")
if provider != "link" && provider != "agentcard" {
return nil, fmt.Errorf("--provider must be link or agentcard")
if provider != "link" && provider != "agentcard" && provider != "kernel" {
return nil, fmt.Errorf("--provider must be link, agentcard, or kernel")
}
raw, _ := cmd.Flags().GetString("spec")
var spec map[string]json.RawMessage
Expand All @@ -393,6 +418,9 @@ func vaultSpecFromFlags(cmd *cobra.Command) (map[string]json.RawMessage, error)
if vaultSpecHasSecrets(json.RawMessage(raw)) {
return nil, fmt.Errorf("--spec must not contain credentials or tokens; use the dedicated file/stdin inputs")
}
if provider == "kernel" && vaultSpecHasCardData(json.RawMessage(raw)) {
return nil, fmt.Errorf("--spec must not contain card details; use hosted enrollment")
}
spec["provider"], _ = json.Marshal(provider)
return spec, nil
}
26 changes: 25 additions & 1 deletion cmd/vaults_help.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ or normalization. Never include card data, OAuth tokens, or provider secrets in
`

const vaultWalletSpecHelp = `
Omit config selection to preserve Kernel-managed defaults.
For --provider kernel, use --spec '{}'. Share the returned card_enrollment URL
with the cardholder; never enter card details in the CLI. Use wallets get --wait 60
to observe connection, then wallets payment-methods to inspect token eligibility.
A connected wallet may still be ineligible while network token enrollment runs.
Kernel wallets accept neither provider configuration flags nor --tokens-file.
For Link and AgentCard, omit config selection to preserve managed defaults.
--provider-config-id and --provider-config-name are mutually exclusive.
For Link with selection flags, use --spec '{}' and --tokens-file <path|->.
Alternatively, set the customer_managed client and provider_config in --spec,
Expand All @@ -31,6 +36,8 @@ an uncertain payment through the new wallet. There is no in-place reauthorizatio

type ProviderConfigReference = { id: string } | { name: string };

type KernelWalletSpec = { provider: "kernel" };

type LinkWalletSpec = {
provider: "link";
authorization: {
Expand All @@ -48,6 +55,16 @@ type AgentCardWalletSpec = {
`

const vaultCardSpecHelp = `
type KernelCardSpec = {
provider: "kernel";
wallet: string; // enrolled Kernel wallet item key
amount: number; // integer minor units; 1..50000
currency: string; // ISO 4217 three-letter code
merchant_name: string; // 1..255 characters
merchant_url: string; // HTTPS checkout URL; fill locked to its exact origin
merchant_country?: string; // ISO 3166-1 alpha-2; required for Visa
};

type LinkCardSpec = {
provider: "link";
wallet: string; // wallet item key
Expand Down Expand Up @@ -91,6 +108,13 @@ type LinkTotal = {
amount: number; // integer minor units
};

Kernel cards cannot be updated. Create a new card item for a new purchase, but
never retry an uncertain authorization or replace an item in recovery_required.
Invoke authorize only when advertised. For Visa, present the returned
spend_approval URL to the cardholder and poll items get --wait 60 until ready.
Mastercard may become ready without hosted approval. Neither ready nor fill
confirms merchant payment. Fill and submit before expires_at; never pass PAN to CLI.

Card updates replace the whole spec, so omitting checkout_origin from an update removes
its existing value. For non-prepared authorization, checkout_origin is forwarded to
AgentCard for eligible autopilot rule matching. Use a canonical HTTPS origin (lowercase
Expand Down
Loading
Loading