Skip to content
Merged
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
2 changes: 1 addition & 1 deletion plugins/kbagent/agents/keboola-expert.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ read it when a trigger fires. Each `(X.Y.Z+)` tag is the version floor.
KMS -- ciphertext does NOT cross projects.
- **`ENCRYPTION_FAILED` on an Azure stack is a VERSION GATE, not a bad token**:
<= 0.85.0 rejected the Azure `KBC::ProjectSecureKV::` cipher, so private-repo
`create` and `secrets-set` could not work there at all. Upgrade to 0.85.1+; do
`create` and `secrets-set` could not work there at all. Upgrade to 0.86.0+; do
NOT reach for `--allow-plaintext-on-encrypt-failure` (it writes the PAT in
clear). gotchas.md § Encryption ciphertext.
- **`validate-repo`** (0.29.0+): GitHub-only, `--type python-js` only, <=5 API
Expand Down
10 changes: 5 additions & 5 deletions plugins/kbagent/skills/kbagent/references/gotchas.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,15 +396,15 @@ Versioning convention:
`--set 'parameters.#password=...'` or a full `--configuration`. (Either way
the value is now encrypted before write.)

## Encryption ciphertext has a different prefix per cloud; Azure was rejected (since v0.85.1, #607)
## Encryption ciphertext has a different prefix per cloud; Azure was rejected (since v0.86.0, #607)

- **One project-scoped prefix per cloud.** The Encryption API returns
`KBC::ProjectSecure::` on AWS, `KBC::ProjectSecureGKMS::` on GCP and
`KBC::ProjectSecureKV::` on Azure (Key Vault). All three mean the same
thing -- project-bound ciphertext -- and none of them is interchangeable
across stacks. Never assert on a single literal prefix when checking
whether a value is encrypted; match the whole family, or just `KBC::`.
- **Before v0.85.1 the Azure variant was missing from the data-app
- **Before v0.86.0 the Azure variant was missing from the data-app
whitelist**, so on an Azure stack a *correctly* encrypted value was
rejected as "not project-scoped" and the write aborted with
`ENCRYPTION_FAILED` -- before anything reached Storage. Affected:
Expand All @@ -417,7 +417,7 @@ Versioning convention:
/ `KBC::ConfigSecure*` scopes and plaintext are still refused, and plaintext
is still never written to Storage.
- **The sync side had the same gap (#612).** `sync diff` / `sync status`
redact ciphertext so it never shows up as a diff; until 0.85.1 the detector
redact ciphertext so it never shows up as a diff; until 0.86.0 the detector
knew only the AWS-form prefixes, so on GCP/Azure an encrypted value stored
under a **non-`#` key** was treated as plaintext. It now derives the whole
family from the platform registry (8 scopes x 3 clouds + 3 legacy ciphers).
Expand Down Expand Up @@ -1194,10 +1194,10 @@ events and emits a final `done` SSE frame mirroring the same record.
validator is wrong -- upgrade rather than reaching for `--no-validate`,
which switches off the checking that still works.
- **A body with no `parameters` key is validated as an EMPTY `parameters`
section (since v0.85.1, issue #605).** So a body that forgot the wrapper --
section (since v0.86.0, issue #605).** So a body that forgot the wrapper --
the component's own fields sitting at the configuration root -- now fails
with the schema's required-field errors plus a `hint:` line naming the
missing wrapper. **Before v0.85.1 it validated `ok` and was POSTed
missing wrapper. **Before v0.86.0 it validated `ok` and was POSTed
verbatim**, producing a live configuration with no `parameters` key, which
the UI and the component runtime both read as empty (blank boilerplate) even
though `--push` reported success. The whole-body exemption is now keyed on
Expand Down
63 changes: 57 additions & 6 deletions src/keboola_agent_cli/changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
# Ordered newest-first. Each value is a list of brief one-line descriptions.
CHANGELOG: dict[str, list[str]] = {
"0.86.0": [
"New read-only `kbagent notification` command group audits Flow "
"New: read-only `kbagent notification` command group audits Flow "
"Notifications-tab recipients across projects. Subcommands: `notification list` "
"and `notification detail`. Those recipients (the bell icon -- Success / Error / "
"Processing-delay / Warning cards) are stored in a separate platform service, not "
Expand All @@ -35,7 +35,8 @@
'in-flow `type: "notification"` TASK is a different mechanism and stays visible '
"via `flow detail`. Authenticates with the plain project Storage token every "
"registered alias already holds -- no elevated scope, no manage token.",
"`notification list` counts the project-wide subscriptions its scope filters hide, "
"Note: `notification list` counts the project-wide subscriptions its scope filters "
"hide, "
'so "who gets paged" is never under-reported. `--component-id` / `--config-id` '
"filter client-side -- as does `--event`: the service accepts its documented "
"`?event=` parameter and then ignores it, answering 200 with the project's full "
Expand All @@ -48,15 +49,65 @@
"`job-processing-long` and the `phase-job-*` variants); `--event` is forwarded "
"verbatim and deliberately not validated against that list, because the API "
"declares `EventName` as an open string.",
"`kbagent serve` mirrors the new group 1:1: `GET /notifications` and "
"New: `kbagent serve` mirrors the new group 1:1: `GET /notifications` and "
"`GET /notifications/{project}/{subscription_id}`.",
'A filled Branch column on `notification list` does not mean "dev-branch only". '
'Note: a filled Branch column on `notification list` does not mean "dev-branch only". '
"The Flow Builder writes a `branch.id` filter on EVERY subscription, and for a "
"production one that value is the default branch's own numeric id -- so `branch_id` "
"is populated on every row, production included. Cross-check `kbagent branch list` "
"for the project to tell a production alert from a dev-branch one.",
],
"0.85.1": [
"New: `kbagent token list --project NAME` lists a project's Storage tokens "
"without opening the web UI (issue #599). Each row carries id, description, "
"created, expires (with an expired marker), the master flag and which token "
"minted it -- and that id is the `--token-id` that `token delete` / `token "
"refresh` need, which nothing in the CLI could previously tell you. Secret "
"values are STRIPPED from every row before output, including under `--json`: a "
"project carrying the `force-decrypted-token` feature has the API embed live "
"secrets in the listing, and echoing those would break the group's "
'"revealed once, at mint" contract for every token at once. Wired through all '
"layers -- client, service, CLI, `kbagent serve` (`GET /token/{project}/list`), the "
"permission registry (`cli:read`) and the importable SDK facade "
Comment thread
padak marked this conversation as resolved.
"(`Client.list_tokens()` -> `TokenListEntryResult`).",
"Change: a 5xx response (and a read/write timeout) is now retried ONLY on the "
"RFC 9110 idempotent methods -- GET, PUT, DELETE, HEAD, OPTIONS, TRACE. It is no "
"longer retried on POST or PATCH. Keboola's token mint persists the token row "
"before the step that can throw and wraps none of it in a transaction, so a "
"retried `POST /v2/storage/tokens` could leave live credentials behind that the "
"caller never sees -- a silent secret leak dressed as a failed command. The fix "
"covers all 29 POST call sites, not just the token mint. A 429 stays retryable on "
"every method (the server states it did not process the request), as do a refused "
"connection and a connect/pool timeout. A 500 on an unretried write now reports "
"`retryable=false`. After such a failure, verify with a read (`token list`, "
"`config detail`, `job list`) before repeating the command -- the write may well "
"have landed.",
"Change: a 5xx that reports only `Application error.` now surfaces its "
"`exceptionId` too. That id is the only handle Keboola support can trace an "
"incident by, and the generic body used to swallow it. The id is bounded and "
"character-filtered before being echoed (it "
"is untrusted input reaching a Rich-markup console) and carries one of two hints: "
"the request was not retried because its method is not idempotent (verify before "
"repeating), or the same 5xx survived every attempt (upstream incident -- escalate "
"with the id).",
"Internal: the Layer 3 client gains a `merge_requests` namespace (DMD-1701 / "
"DMD-1833). It covers the nine dev-branch merge-request endpoints: `list`, `get`, "
"`conflicts`, `create`, `update`, `request_review`, `approve`, `request_changes` "
"and `merge`. "
"No CLI command or `serve` route is exposed yet; this is the client groundwork a "
"later release builds on. Two invariants deliberately break the surrounding "
"idioms and are documented in the docstrings: merge-request paths are NEVER "
"branch-prefixed (every MR endpoint is project-level), and bodies are sent as JSON "
"with real types (the backend asserts `branchFromId` as an int, and a "
"form-encoded string fails its validation). `merge()` awaits the Storage job with "
"a dedicated 600 s budget, because merging a many-config branch outlives the "
"default 60 s.",
"Note: the `winget` job is disabled in the release pipeline until the package is "
"bootstrapped in microsoft/winget-pkgs (#610). `wingetcreate update` cannot bump a "
"manifest that was never first submitted, so the job failed on every stable tag "
"and left the whole pipeline permanently red -- masking real release failures. The "
"job body and its bootstrap NOTE are kept, and the original `if:` condition "
"survives as a comment for one-line re-enablement. WinGet users stay on the last "
"published version until the manifest is submitted; every other channel "
"(uv/pip, Homebrew, Chocolatey, apt, dnf, zip) is unaffected.",
"Fix: `kbagent config new --push` no longer creates a broken configuration from a "
"body that forgot the `parameters` wrapper (#605). A component's "
"`configurationSchema` describes the CONTENTS of `configuration.parameters`, so a "
Expand Down
2 changes: 1 addition & 1 deletion src/keboola_agent_cli/services/data_app_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def _default_ds_client_factory(stack_url: str, token: str) -> DataScienceClient:
# https://developers.keboola.com/overview/encryption/. A fourth entry,
# ``KBC::ProjectSecureKMS::``, was carried here from 0.27.0 but appears nowhere
# in the platform -- the AWS wrapper is *named* ``PrefixProjectKMS`` while the
# prefix it emits is plain ``KBC::ProjectSecure::``. Dropped in 0.85.1 (#607).
# prefix it emits is plain ``KBC::ProjectSecure::``. Dropped in 0.86.0 (#607).
ENCRYPTED_PASSWORD_PREFIXES: tuple[str, ...] = (
"KBC::ProjectSecure::",
"KBC::ProjectSecureGKMS::",
Expand Down
2 changes: 1 addition & 1 deletion src/keboola_agent_cli/sync/secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
# Key Vault appends ``KV`` and Google KMS appends ``GKMS`` -- so one scope is
# three prefixes: ``KBC::ProjectSecure::``, ``KBC::ProjectSecureKV::``,
# ``KBC::ProjectSecureGKMS::``. Listing only the AWS forms (as this module did
# until 0.85.1) made every GCP/Azure ciphertext read as plaintext here -- see
# until 0.86.0) made every GCP/Azure ciphertext read as plaintext here -- see
# issue #612, and #607 for the same defect in the data-app write path.
_CIPHER_SCOPES: tuple[str, ...] = (
"Secure",
Expand Down