feat(0.28.0): data-app secrets + validate-repo + --auth public fix (logs deferred) - #242
Closed
ottomansky wants to merge 1 commit into
Closed
Conversation
…ogs deferred to follow-up) Closes the secrets-management, pre-flight-validation, AND public-auth-mode gaps in the v0.27.0 data-app surface. Logs / auto-log-dump deferred pending platform-side API exposure (the Data Science API does not document a programmatic logs endpoint; data_app_service.py and data-app-workflow.md already commit to its absence -- tracked as issue keboola#240). The auth fix was discovery-driven: while live-validating secrets-set end-to-end on project 1143, --auth public surfaced HTTP 503 against the Keboola app-proxy. Root cause (no authorization key written by v0.27.0) took 30min against the public AppProxyDefinition.php validator; fixing it in this PR keeps v0.27.0 -> v0.28.0 from shipping a known silent break. ## Surfaces (all v0.28.0) - Service: DataAppService.{set,list,get,remove}_data_app_secrets with read-modify-write at the service layer (NOT Storage merge=True) to preserve nested sibling keys; per-project KMS encryption fail-closed; metadata-only on get (never echoes decrypted plaintext); idempotent remove; reserved-name shadowing detection. - Service: RepoValidateService + GitHubContentsClient for pre-flight Golden-Rule check. Trees-recursive fetch + up to 4 contents calls (<=5 GitHub API calls regardless of repo size). --type python-js only. - Service: _build_public_auth_block() + _auth_block_for(auth) dispatch. v0.27.0's --auth public wrote no authorization key at all -- the Keboola app-proxy refused to route (HTTP 503) and the UI's auth selector showed blank. Now writes the canonical noneProxyAuthorization shape ({auth_providers: [], auth_rules: [{auth_required: false}]}). Authoritative source: the public backend validator at keboola/job-queue-job-configuration AppProxyDefinition.php (when auth_required=false, auth MUST NOT be set). The private keboola/ui apps/kbc-ui/src/scripts/modules/data-apps/constants.ts corroborates with its noneProxyAuthorization constant for the "None" UI option (Keboola org members only). Live-validated end-to-end on project 1143: HTTP 200 on the resulting URL, no auth challenge, written block bit-identical to canonical. - Commands: kbagent data-app secrets-{set,list,get,remove} + kbagent data-app validate-repo. Reference: epilogs on every docstring. - Errors: DATA_APP_INVALID_SECRET, DATA_APP_INVALID_REPO, DATA_APP_REPO_VALIDATION_BLOCKING. - Permissions: secrets-set=write, secrets-list/get=read, secrets-remove=destructive, validate-repo=read. - Hints: 5 new --hint client/service variants. - Tests: 27 secrets service tests + 20 validate-repo service tests + 22 CLI tests (13 methods + 9 hint-compile parametrised) + 4 new auth-block tests (2505 total, all green). - Docs: CLAUDE.md All CLI Commands, AGENT_CONTEXT, keboola-expert.md matrix + version gate + 4 inline gotchas, commands-reference.md bullets, gotchas.md (three new (since v0.28.0) entries: auth fix, secrets, validate-repo), data-app-workflow.md (Managing app-runtime secrets + Pre-flight repo validation recipes). - Plugin: SKILL.md regenerated; plugin.json + marketplace.json synced to 0.28.0; changelog.py entry. Reserved-runtime-env-vars list locked to canon-documented floor (KBC_TOKEN, KBC_URL) per https://help.keboola.com/data-apps/storage-access/; TODO in gotchas to verify exhaustive list against running data-app env in follow-up. Out of scope (orphan-prevention issues filed BEFORE merge): - Logs / auto-log-dump on deploy failure -> keboola#240 (needs platform API). - --auth oidc / github / gitlab / jumpcloud / auth0 -> keboola#241.
Member
|
Closing in favor of new PR from padak/keboola_agent_cli — branch has been rebased onto feat-0.29.0 and will be merged from there. |
This was referenced May 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why these three changes travel together
v0.28.0 rounds out the v0.27.0 data-app surface with three changes that
travelled together because the third was discovered while live-validating
the first two. Running the new
secrets-setagainst a real--auth publicdata app surfaced that v0.27.0's
--auth publicwrites noauthorizationkey into the Storage config — the Keboola app-proxy returns HTTP 503 and
the UI's auth-type selector shows blank. Root-cause took 30 minutes
against the public
AppProxyDefinition.phpvalidator; fixing it in thisPR keeps v0.27.0 → v0.28.0 from shipping a known silent break. All three
changes touch the same
parameters.dataApp.*write paths and share theread-modify-write / fail-closed encryption / metadata-only-read patterns;
they are not three independent features.
Logs / auto-log-dump on deploy failure was the originally-planned fourth
surface. It is deferred because the Data Science API does not document
a programmatic logs endpoint (the Terminal Log
tab help canon
documents only an in-UI "Download Logs" button). Tracked as
#240 — needs
platform-side API exposure first.
What changed
Surface 1 —
kbagent data-app secrets-set / -list / -get / -remove#-prefixed app-runtime secrets inparameters.dataApp.secretson a
keboola.data-appsStorage config.v0.27.0's
--git-pat-encrypted: if the Encryption API does not returna
KBC::Project*ciphertext, the command aborts withENCRYPTION_FAILEDbefore Storage is touched.--allow-plaintext-on-encrypt-failureis bootstrap/debug only.merge=True(which is shallow at the top level only and would clobber sibling keys
nested inside
parameters.dataApp.secrets). Every untouched key inthe config body is preserved bit-identical — asserted by
tests/test_data_app_secrets_service.py::TestSetSecretsHappyPath::test_sibling_keys_preserved_bit_identical.secrets-getis metadata-only. NEVER echoes the decrypted plaintextto stdout / stderr / logs / change descriptions. The Encryption API has
no decrypt endpoint; the CLI does not attempt to decrypt under any
branch. NOT_FOUND on an absent key never enumerates sibling keys
(avoids leaking neighbour presence).
secrets-removeis idempotent. Removing a key that isn't set isexit 0 with
removed: 0and no Storage write.env-var name collides with
RESERVED_RUNTIME_ENV_VARS(canon-documentedfloor:
KBC_TOKEN,KBC_URLperhttps://help.keboola.com/data-apps/storage-access/) emits a stderr WARN
and surfaces
shadowed_by_runtimein the JSON envelope. The writestill happens — the platform shadows the value at runtime, not at
write time.
strip
#, replace-with_, uppercase.#my-api-key→MY_API_KEY.Surface 2 —
kbagent data-app validate-repo≤5 calls regardless of repo size (1 trees-recursive + up to 4
contents fetches), so the 60/hour unauth GitHub rate limit is no
longer the common-case failure mode.
help-doc anchor that defines the rule
(https://help.keboola.com/data-apps/python-js/).
--type python-jsonly in 0.28.0; streamlit / pure-Python / R /Node-only follow-up.
Surface 3 —
--auth publicfixauthorizationkey when--auth public, leaving theKeboola app-proxy unable to route (HTTP 503 / "Service Unavailable")
and the UI's "Authentication Type" selector blank.
noneProxyAuthorizationshape:{ "app_proxy": { "auth_providers": [], "auth_rules": [ { "type": "pathPrefix", "value": "/", "auth_required": false } ] } }keboola/job-queue-job-configurationAppProxyDefinition.php.The validator's invariants (
auth_providersmay be empty;auth_rulesrequires ≥1 element;auth_required: falserequiresauthto NOT be set) make the shape this PR writes the uniquesolution.
keboola/uiapps/kbc-ui/src/scripts/modules/data-apps/constants.tsexports this exact shape as
noneProxyAuthorizationfor the "None"UI option. Mentioned for internal verifiability — the public validator
above is sufficient for external readers; no clickable URL is provided
for the UI repo because it is not externally accessible.
--auth passwordbehaviour unchanged.Other auth providers (OIDC / GitHub OAuth / GitLab OAuth / JumpCloud /
Auth0) are NOT yet exposed by the CLI's
--authflag — tracked as#241.
Plumbing
ErrorCodeentries:DATA_APP_INVALID_SECRET,DATA_APP_INVALID_REPO,DATA_APP_REPO_VALIDATION_BLOCKING.data-app.secrets-set(write),data-app.secrets-list/
data-app.secrets-get(read),data-app.secrets-remove(destructive),data-app.validate-repo(read).--hint client/servicedefinitions; all 9 hint snippetsast.parsecleanly (parameterized test in
tests/test_data_app_secrets_cli.py).KeboolaApiErrorcatch sites incommands/data_app.pynow propagatedetails=exc.detailstoformatter.error(was: dropped silently). Theservice populates
details={"project_alias": ..., "failed_keys": [...]}on
ENCRYPTION_FAILEDso the operator can recover the failed-key listfrom the JSON envelope.
_auth_block_for(auth)dispatch helper raisesValueErroron unknownvalues rather than returning
None— structurally prevents anotherv0.27.0-style silent-503 regression if a future auth mode is added to
_validate_create_inputsbut not to the dispatch._redact_secrets_blockextends the existing_redact_storage_confighelper so
data-app detailJSON output never echoes encryptedciphertext from the secrets sub-dict.
services/repo_validate_service.pycarries theRepoValidateService+GitHubContentsClient+ purevalidate_keboola_repofunction (no I/O); the rest of kbagent staysKeboola-API-only.
Sync-map walk
Every silent-drift surface from
CONTRIBUTING.md"Plugin synchronizationmap" updated:
pyproject.toml0.27.0 → 0.28.0;plugin.json+marketplace.jsonsynced viamake version-sync.changelog.py— one entry-list for 0.28.0 covering all three surfaces.commands/context.pyAGENT_CONTEXT— 5 new command sigs.CLAUDE.md## All CLI Commands— 5 new lines.keboola-expert.mdmatrix — 5 new rows; §1 Rule 6 VERSION GATE notessecrets / validate-repo need 0.28.0+; §3 Inline Gotchas — new--auth public+secrets-* metadata-only+validate-repo GitHub-onlyentries.commands-reference.md— 5 new bullets.gotchas.md— three new(since v0.28.0)entries (auth fix; secrets;validate-repo).
data-app-workflow.md— "Managing app-runtime secrets" + "Pre-flightrepo validation" recipe sections; "When to use what" table extended;
the existing "deliberately does NOT cover the build/runtime log" note
links to issue data-app logs + auto-log-dump on deploy failure #240.
SKILL.mdregenerated viamake skill-gen.Verifying the auth fix end-to-end
Live validation (project 1143, on stack europe-west3.gcp)
All scenarios PASS. Verbatim CLI output captured in
~/.claude/plans/validation-output.md(442 lines); summary table:data-app create --waitstate == runningon first pollsecrets-setnormal + reserved-name (#KBC_TOKEN=foo)shadowed_by_runtime: ["KBC_TOKEN"]in JSON; both secrets writtensecrets-list±--show-fingerprint--show-fingerprint: 8-char fingerprint +KBC::ProjectSecureGKMSprefix onlysecrets-getplaintext-absence canarygrep -F -c 'hello'across stdout AND stderr in BOTH JSON and human modes: 0 hitsdata-app deploy --wait(§9 redeploy contract)secrets-remove+ redeploy#TEST_KEYgone, version 5 → 6, deployed=6secrets-remove(--key '#NONEXISTENT')removed: [], no version bumpvalidate-repoagainstkeboola/example-python-js-hello-worldrequires-pythonskip), 0 BLOCKING, exit 0validate-repoagainsttorvalds/linuxmeta.tree-truncated(>100k entries),is_failure: trueerror_code--auth publicpost-fix HTTP 200authorizationblock bit-identical to canon;curl→ HTTP 200 first attemptTest plan
make check— green: lint + format + skill freshness + versionconsistency + changelog + error-codes + 2505 tests pass
(5 e2e skipped, 64 deselected as
not e2e)--auth publicre-validation — HTTP 200, bit-identical to canon(
TestSetSecretsHappyPath::test_sibling_keys_preserved_bit_identical)ast.parsecleanly--auth publicapp#KBC_TOKENlisted (encrypted ciphertext) after asecrets-setwriteE2E_API_TOKEN+E2E_URLOut of scope (orphan-prevention issues filed before merge)
data-app logs+ auto-log-dump on deploy failure → tracked asdata-app logs + auto-log-dump on deploy failure #240 (OPEN; needs platform-side API exposure first — the Data
Science API does not document a programmatic logs endpoint per the
Terminal Log tab help canon).
--auth oidc/--auth github/--auth gitlab/--auth jumpcloud/
--auth auth0→ tracked as data-app: extend --auth flag to OIDC / GitHub OAuth / GitLab OAuth / JumpCloud / Auth0 #241 (OPEN; per-provider configschemas need a separate design pass for
client_secretencryption,per-provider flags, and live-validation against each external service).
data-app validate-repo --typefor non-python-jsrepos andnon-GitHub hosts → noted in data-app: extend --auth flag to OIDC / GitHub OAuth / GitLab OAuth / JumpCloud / Auth0 #241's scope adjacent.
Suggested labels (maintainer to apply)
enhancementis the most relevant existing label on this repo. The PRalso touches
area:data-appif/when that label exists.gh issue edit --add-labelfailed with permission errors when filing #240/#241 fromthe fork; padak (or any collaborator) can apply labels post-open.
Files changed
23 files, +4093 / -24:
make checkclean, single commit, force-pushed-with-lease.