From e5f708507eec00d527c47749da47d145b6caf9cc Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 22 Sep 2026 15:11:58 +0200 Subject: [PATCH 01/15] AJDA-3380: fix the Google OIDC tab after the usability test Michal's 40-minute run through the Google tab, checked against Google's own docs, turned up one wrong claim and a row of snags: - Testing does not gate sign-in for Keboola apps. The proxy requests only openid, email and profile (oauth2-proxy default; keboola-as-code sets no scope), and Google exempts exactly those scopes from the test-user list. The page said the opposite. Now: Internal limits sign-in to the org, External lets any Google account in, publishing is irrelevant. - Step 1 starts from an existing app, however it was built; Kai keeps the OIDC settings on later changes (MCP authentication_type=default). - Direct links to the Google Auth Platform pages; both states of the consent screen (Get started vs already configured); the exact redirect URI field with a filled-in example; the error headline Google actually shows and where Request details lives; the encrypted KBC::ProjectSecureGKMS:: value after Save; Start App vs Redeploy App; test in a private window. - Admin-rights prerequisite up front; Kai and kbagent cannot do this part. - "You now have" checkpoints on every provider tab. Co-Authored-By: Claude Fable 5.1 --- src/content/docs/data-apps/authentication.mdx | 50 +++++++++++-------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/src/content/docs/data-apps/authentication.mdx b/src/content/docs/data-apps/authentication.mdx index 321087119..1aa001141 100644 --- a/src/content/docs/data-apps/authentication.mdx +++ b/src/content/docs/data-apps/authentication.mdx @@ -41,13 +41,13 @@ OIDC lets users log into your app through your single sign-on (SSO) provider. Ke ![The app's sign-in page asking the user to select an authentication provider, one button per configured provider](/data-apps/auth-select-oidc-provider.png) -The flow is the same everywhere: create the app in Keboola, register it with your provider using the app's callback URL, paste the provider's credentials into the app's **Authentication** settings, and deploy. +The flow is the same everywhere: open the app in Keboola, register it with your provider using the app's callback URL, paste the provider's credentials into the app's **Authentication** settings, and deploy. It's a few minutes of clicking in two browser tabs, and it needs admin rights in the provider's console: for Google, the **Owner** or **OAuth Config Editor** role on a Google Cloud project; for the others, the right to register applications in your tenant. Kai and kbagent can't do this part for you. -### Step 1 — Create the app and copy its callback URL +### Step 1 — Open the app and copy its callback URL -Your provider needs the app's callback URL, so create the app first. +Your provider needs the app's callback URL, so start in Keboola. -1. In your Keboola project, open **Apps**, click **+ Create App**, and [create the app manually](/data-apps/getting-started/#create-an-app-manually). The app opens on its configuration page. (Adding sign-in to an existing app? Open its configuration instead.) +1. In your Keboola project, open **Apps** and click the app. It doesn't matter whether Kai built it or you created it yourself, and Kai keeps these settings when it changes the app later. No app yet? [Create one manually](/data-apps/getting-started/#create-an-app-manually) first; it opens on its configuration page. 2. Scroll to the **App URL** block. It shows the app's host as a URL prefix plus a generated part, for example `toy-store-sales` and `-74016144.hub.europe-west3.gcp.keboola.com`. Your callback URL is `https://`, that whole host, and `/_proxy/callback`: ``` @@ -58,9 +58,11 @@ Your provider needs the app's callback URL, so create the app first. The block is there from the moment the app exists; you don't have to deploy first. +The block sits on the app's **Overview** tab, below **Authentication**: + ![The app's configuration page with the App URL block: the URL prefix, the generated host, and a copy button](/data-apps/publish-config.png) -Keep this tab open. Each app has its own callback URL, so register every app with your provider separately. +Keep this tab open. You now have the callback URL; each app has its own, so register every app with your provider separately. ### Step 2 — Set up your identity provider @@ -70,32 +72,33 @@ Pick your provider: Let people sign in with their Google account. - **Before you start** — you need a Google Cloud project where you can manage the consent screen and OAuth clients: the project **Owner** role, or the **OAuth Config Editor** role. Decide who should get in, too. To limit sign-in to your Google Workspace organization, the Google Cloud project must belong to that organization and you pick the **Internal** audience below. Otherwise the audience is **External**: any Google account can sign in once you publish the app. + **Before you start.** The OAuth client lives in a Google Cloud project, not in the Google Workspace admin console, so you need a project where you can manage the consent screen and OAuth clients: the **Owner** role, or the **OAuth Config Editor** role. Decide who should get in, too. With the sign-in scopes Keboola requests, an **Internal** audience limits sign-in to your Google Workspace organization (the project must belong to that organization), and an **External** audience lets in anyone with a Google account. + + **Set up the consent screen.** Google keeps it under **Google Auth Platform**; open it directly at [console.cloud.google.com/auth/overview](https://console.cloud.google.com/auth/overview) and pick your project. - **Set up the consent screen:** + 1. If the page shows **Get started**, the project has no consent screen yet. Click it and fill in the wizard: **App name** and **User support email**, the **Audience** (**Internal** or **External**, see above), a contact email, then **Create**. If you see **Branding**, **Audience**, and **Clients** in the left menu instead, the consent screen already exists; carry on. + 2. Open [**Branding**](https://console.cloud.google.com/auth/branding). Under **Authorized domains**, add `keboola.com`, the domain your App URL belongs to, and save. Without it, Google refuses the redirect URI in the next step. + 3. Check [**Audience**](https://console.cloud.google.com/auth/audience): **Internal** for your organization only, **External** for any Google account. Leave the **Test users** list and **Publish app** alone. For the sign-in scopes Keboola uses, Google applies neither: an External app lets any Google account in while still in Testing. Publishing only matters if you want your logo on the consent screen, and then Google asks for branding details and a verification review. - 1. Open the [Google Cloud console](https://console.cloud.google.com/), select your project, and open **Google Auth Platform** (search for it in the console's top bar). - 2. First time in this project? Click **Get started** and fill in the wizard: **App name** and **User support email**, the **Audience** (**Internal** for your Google Workspace organization only, **External** for any Google account), and a contact email, then click **Create**. - 3. Open **Branding**. Under **Authorized domains**, add `keboola.com`, the domain your App URL belongs to, and save. If Google refuses the redirect URI in the next step, it's because the domain is missing here. - 4. Chose **External**? Open **Audience** and add yourself and your testers under **Test users** (up to 100), or click **Publish app** to let anyone with a Google account sign in. Until you do one of these, Google turns everyone else away. + You now have: a consent screen with `keboola.com` authorized and the audience you want. **Create the OAuth client:** - 1. Open **Clients** and click **Create client**. + 1. Open [**Clients**](https://console.cloud.google.com/auth/clients) and click **Create client**. 2. Set **Application type** to **Web application** and give the client a name, for example `Keboola app - Toy store sales`. - 3. Under **Authorized redirect URIs**, click **Add URI** and paste the callback URL from step 1. It has to match exactly: `https`, the full host, and `/_proxy/callback` with no trailing slash. + 3. Under **Authorized redirect URIs**, click **Add URI** and paste the callback URL from step 1, for example `https://toy-store-sales-74016144.hub.europe-west3.gcp.keboola.com/_proxy/callback`. It has to match exactly: `https`, the full host, `/_proxy/callback`, no trailing slash, no stray spaces. 4. Click **Create**. Copy the **Client ID** and the **Client secret** now; Google shows the secret only at creation. If you lose it, open the client and click **Add Secret**. - **Back in Keboola** — on the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Google SSO** in the **Provider** dropdown, and paste the **Client ID** and **Client secret**. There's no issuer field for this option; Keboola uses `https://accounts.google.com`. Click **Save**. + You now have: a Client ID and a Client secret. + + **Back in Keboola.** On the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Google SSO** in the **Provider** dropdown, paste the **Client ID** and **Client secret** (this option has no issuer field), and click **Save**. After saving, the secret field shows an encrypted value starting with `KBC::ProjectSecureGKMS::`. That's your secret stored encrypted, not a replacement; to change it later, paste a new value over it. **If sign-in fails:** - - `Error 400: redirect_uri_mismatch` — the URI in the OAuth client differs from the app's callback URL. Compare them character by character (scheme, host, `/_proxy/callback`, no trailing slash) and fix the client. - - "Access blocked: … has not completed the Google verification process", or a colleague can't get past Google — the audience is External and the app is still in **Testing**, so only listed **Test users** can sign in. Add them on the **Audience** page, or click **Publish app**. + - **Access blocked: This app's request is invalid**, with `Error 400: redirect_uri_mismatch` — the URI in the OAuth client differs from the app's callback URL. Open **Request details** on that page: it shows the `redirect_uri` Google received. Compare it with the **App URL** block character by character (a stray space or a missing character is the usual cause) and fix the client. No redeploy needed; Google says such changes take from a few minutes to a few hours to apply. - Someone outside your organization can't sign in — expected with the Internal audience. Switch to External on the **Audience** page if that's not what you want. + - Someone outside your organization *can* sign in — the audience is External. For the scopes Keboola uses, Google doesn't limit an External app to its test users, even in Testing. Switch to Internal. - `invalid_client`, or a token error right after signing in — the Client ID or Client secret in Keboola doesn't match the OAuth client. Paste them again, or add a new secret in Google Cloud and update the app. - - Changed the redirect URI or the audience on Google's side later? No redeploy needed; Google says such changes take from a few minutes to a few hours to apply. Let people sign in with their Microsoft work account. @@ -116,6 +119,8 @@ Pick your provider: 2. Enter a description, pick an expiry, and click **Add**. 3. Copy the secret's **Value** right away; Entra hides it once you leave the page. Note the expiry, too: before it passes, create a new secret and update the app's authentication settings, or sign-in stops working. + You now have: an Application (client) ID, a Directory (tenant) ID, and a client secret Value. + **Optional — restrict who can sign in.** Out of the box, anyone in your tenant can open the app. To let in only specific people or groups, go to **Entra ID → Enterprise apps**, open your app, and under **Manage → Properties** set **Assignment required?** to **Yes**. Then, under **Manage → Users and groups**, click **Add user/group** and pick who may sign in. Assigning groups, rather than single users, needs a Microsoft Entra ID P1 or P2 license. Everyone else now gets `AADSTS50105` at sign-in. **Back in Keboola** — on the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Azure OIDC** in the **Provider** dropdown, and paste the **Client ID**, **Client secret**, and **Tenant ID**. Keboola builds the issuer from the tenant ID (`https://login.microsoftonline.com//v2.0`). Click **Save**. @@ -140,6 +145,8 @@ Pick your provider: 5. Under **Assignments**, decide who can sign in: **Allow everyone in your organization to access** or **Limit access to selected groups** (or skip the assignment for now and add people later). 6. Click **Save**. On the **General** tab, under **Client Credentials**, copy the **Client ID** and the **Client secret**. + You now have: a Client ID and a Client secret, and Okta knows your callback URL. + **Back in Keboola** — on the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Okta** in the **Provider** dropdown, and paste the **Client ID** and **Client secret**. Set **Domain/Org URL** to `https:///oauth2/default`; your Okta domain is shown in the Admin Console when you click your name at the top right, for example `https://acme.okta.com/oauth2/default`. The `default` authorization server comes with the Integrator Free Plan and with API Access Management; if **Security → API → Authorization Servers** doesn't list it, use the org authorization server instead: `https://` with no path. **Logout URL** is optional: `https:///login/signout` also ends the Okta session when someone signs out of the app. Click **Save**. **If sign-in fails:** @@ -161,6 +168,8 @@ Pick your provider: 4. Scroll down to **Application URIs** and paste the callback URL from step 1 into **Allowed Callback URLs**. 5. Click **Save Changes** at the bottom of the page. + You now have: your tenant Domain, a Client ID, and a Client Secret. + **Back in Keboola** — on the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Auth0** in the **Provider** dropdown, and paste the **Client ID** and **Client secret**. Set **Issuer URL** to your tenant **Domain** with `https://` in front and a trailing slash, for example `https://acme.us.auth0.com/` (or a custom domain such as `https://login.acme.com/`). The field's example omits the slash, but Auth0's issuer ends with one; when in doubt, copy the `issuer` value from `https:///.well-known/openid-configuration`. **Logout URL** is optional: `https:///oidc/logout` also ends the Auth0 session when someone signs out of the app. Click **Save**. **If sign-in fails:** @@ -183,10 +192,9 @@ Pick your provider: ### Step 3 — Deploy and test -1. Set the app's code source and click **Deploy App**; the short wizard asks for the backend size and an inactivity timeout. (Details: [Create an app manually](/data-apps/getting-started/#create-an-app-manually). Just testing sign-in? A **Streamlit** app with a one-line inline script is the quickest thing to deploy.) +1. A new app: set its code source and click **Deploy App**; the short wizard asks for the backend size and an inactivity timeout (details: [Create an app manually](/data-apps/getting-started/#create-an-app-manually)). Just testing sign-in? A **Streamlit** app with a one-line inline script is the quickest thing to deploy. An app that's already running needs **Redeploy App** for the new authentication to take effect; a stopped one, **Start App**. 2. When the status turns **Active**, click **Open App**. Your provider asks you to sign in, then sends you into the app. - -Changing the authentication settings of an app that's already deployed? Click **Redeploy App** so the change takes effect. +3. Test with the accounts that matter, each in a fresh private window: one that should get in, one that shouldn't. A browser that's already signed in to the provider skips the sign-in screen and hides the result. ### Provider settings at a glance From 9ce1383ad759672ee5226c1259543bf3c60dc968 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 22 Sep 2026 20:34:31 +0200 Subject: [PATCH 02/15] AJDA-3380: two ways to run an app, Operate and update, Troubleshooting Deliverables 3, 4 and 5 of the Apps GA docs review, text first (screenshots follow the 25 Sep UI freeze): - What are Keboola apps: a "Two ways to run an app" section that explains the managed-repository-with-Kai path against your-own-repository, with a comparison table. Its anchor is the target for the "learn more" link the Builder will show on non-managed-git apps (AJDA-3319). - New page Operate and update an app: reading the state, shipping a change on either path, changing settings and why a redeploy is needed, sleep vs stop, the encrypted secret value, versions, copy/automate/delete. - New page Troubleshooting: where the evidence is (job log, Terminal Logs, kbagent runs/logs, Kai), start failures led by the missing keboola-config folder (AJDA-3334), runtime misbehaviour, sign-in, sleeping. - Overview links the two-paths section; both new pages sit under Run & share in the nav. Co-Authored-By: Claude Fable 5.1 --- _data/navigation.yml | 4 ++ src/content/docs/data-apps/index.md | 2 + src/content/docs/data-apps/operate.md | 52 +++++++++++++++++ src/content/docs/data-apps/troubleshooting.md | 57 +++++++++++++++++++ src/content/docs/data-apps/what-are-apps.md | 19 ++++++- src/sidebar.mjs | 2 + 6 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 src/content/docs/data-apps/operate.md create mode 100644 src/content/docs/data-apps/troubleshooting.md diff --git a/_data/navigation.yml b/_data/navigation.yml index 75164ca72..162bc1158 100644 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -124,6 +124,10 @@ items: title: Authentication - url: /data-apps/publish-and-share/ title: Publish and share + - url: /data-apps/operate/ + title: Operate and update + - url: /data-apps/troubleshooting/ + title: Troubleshooting - url: /data-apps/reference/ title: Reference - url: /data-apps/streamlit/ diff --git a/src/content/docs/data-apps/index.md b/src/content/docs/data-apps/index.md index c3c0d21df..0372b5744 100644 --- a/src/content/docs/data-apps/index.md +++ b/src/content/docs/data-apps/index.md @@ -26,6 +26,8 @@ Dashboards and reports, internal tools, data narratives, configurators, and agen - **[Build with Kai](/data-apps/getting-started/)** — describe the app in plain language and Kai reads your data, builds a live draft you preview and refine, and publishes when you're ready. No coding needed — the walkthrough takes about 10 minutes. The same **Create App** screen also offers [manual creation](/data-apps/getting-started/#create-an-app-manually) if you'd rather configure the app yourself. - **[Build locally](/data-apps/build-locally/)** — develop with your own tools and Git account, use the [AI Kit](/ai/ai-kit/) coding-assistant plugins, and sync the app to your project. For developers who want full control — that page covers how Python/JS development works day to day. +The difference underneath is where the code lives: a repository Keboola manages for Kai, or one you host yourself. [Two ways to run an app](/data-apps/what-are-apps/#two-ways-to-run-an-app) explains what each gives you. + ## Run and share Every app has its own settings page in **Apps**: who can open it ([Authentication](/data-apps/authentication/) — public, password, or your company SSO), its URL and how to [publish and share](/data-apps/publish-and-share/) it, and operational settings like backend size and auto-sleep ([Reference](/data-apps/reference/)). Deploying and redeploying is one click; logs are right on the app's detail. diff --git a/src/content/docs/data-apps/operate.md b/src/content/docs/data-apps/operate.md new file mode 100644 index 000000000..caf192659 --- /dev/null +++ b/src/content/docs/data-apps/operate.md @@ -0,0 +1,52 @@ +--- +title: Operate and update an app +slug: 'data-apps/operate' +description: "Day-to-day work on a deployed Keboola app: read its state, ship a code change, change settings, stop and start it, rotate a secret, delete it." +--- + +Once an app is deployed, everything you do with it starts on its page under **Apps**. This page walks through the routine tasks; [Apps reference](/data-apps/reference/) has the full list of settings and actions. + +## Read the app's state + +The header shows the app's status and the one or two actions that make sense for it: **Deploy App** for an app that has never run, **Start App** for a stopped one, **Open App** and **Redeploy App** for a running one, and **Modify with Kai** on apps Kai built. The **⋯** menu holds the rest: **Copy app**, **Automate** (add it to a flow), **Debug mode**, **Delete app**. + +The tabs below the header split the app's life into views: **Overview** (description, authentication, code source, App URL), **Advanced Settings** (environment variables and secrets, theme, data mappings), **All Runs** (every start attempt), **Terminal Logs** (stdout and stderr while it runs), **Versions** (every configuration change), and **Drafts** while Kai has a draft open. The **App Info** panel on the right shows the backend version and size, the auto-sleep timeout, the owner, and the App ID. + +## Ship a code change + +How depends on where the code lives; see [Two ways to run an app](/data-apps/what-are-apps/#two-ways-to-run-an-app). + +- **Kai built the app.** Click **Modify with Kai** and describe the change. Kai works in a draft with its own live preview; when you're happy, publish it to production. The production app keeps serving the old version until you do. Details: [Build your first app with Kai](/data-apps/getting-started/#how-drafts-become-production). +- **Your own repository.** Push to the branch the app follows, then click **Redeploy App**. Keboola clones the branch again, runs `setup.sh`, and starts the new version; the old one serves until the new one is up. Push alone changes nothing. + +## Change settings + +Authentication, secrets, environment variables, the Git branch, backend size, and the auto-sleep timeout are all edited on the app's page, and none of them takes effect until the app starts again. After saving, click **Redeploy App** (running app) or **Start App** (stopped app). The deploy wizard asks for the backend size and the inactivity timeout every time; keep the current values unless you mean to change them. + +Secrets deserve one note: after you save one, the field shows an encrypted value starting with `KBC::ProjectSecureGKMS::`. That's the secret stored encrypted, not a replacement. To rotate it, paste the new value over the encrypted one and redeploy. How secret names become environment variables: [Secrets](/data-apps/reference/#secrets). + +## Stop, start, sleep + +- **Sleeping** is automatic. After the inactivity timeout (five minutes to 24 hours, set in the deploy wizard) the app suspends; the next visit wakes it and shows a short **waking up** page. You pay only for time the app is awake or waiting to suspend. +- **Stopping** is deliberate: **⋯ → Suspend App** stops the container and takes the URL offline while keeping the configuration. **Start App** brings it back with the same settings. +- **Redeploy App** restarts a running app with the current configuration and, for apps on your own repository, the current branch head. + +Sleep and wake behaviour in detail, including the wakeup error page: [Sleep and resume](/data-apps/reference/#sleep-and-resume). + +## See what changed + +The **Versions** tab lists every configuration change with who made it and when, from "Configuration created" onwards; **Show All Versions** opens the full history. Code changes for a Kai-built app are visible in the Builder's chat and drafts; for your own repository, in your Git history. + +## Copy, automate, delete + +- **Copy app** duplicates the configuration into a new app, so you can try a change without touching the one people use. +- **Automate** adds the app to a flow, for example to redeploy it after the data it reads has been refreshed. +- **Delete app** stops the app and deletes its configuration. The App URL stops working immediately; there is no undo. + +## When something is off + +Start with [Troubleshooting](/data-apps/troubleshooting/): where the logs are, what the common start failures look like, and what to do about each. + +--- + +**Next:** [Troubleshooting →](/data-apps/troubleshooting/) diff --git a/src/content/docs/data-apps/troubleshooting.md b/src/content/docs/data-apps/troubleshooting.md new file mode 100644 index 000000000..d835ded77 --- /dev/null +++ b/src/content/docs/data-apps/troubleshooting.md @@ -0,0 +1,57 @@ +--- +title: Troubleshooting +slug: 'data-apps/troubleshooting' +description: "What to do when a Keboola app doesn't start, doesn't behave, or won't let people in: where the evidence is, the usual causes, and the fix for each." +--- + +Most app problems fall into a handful of patterns. Find where the evidence is first, then your symptom below. + +## Where to look + +- **The deployment job.** Every **Deploy** and **Redeploy** runs as a job, and its event log keeps the container output from the start attempt, including the runtime's own `[ERROR]` lines. Open it from the app's **All Runs** tab or from **Jobs**. +- **Terminal Logs**, the app's tab: stdout and stderr while the app runs. Keboola keeps them only while the app is running. +- **From a terminal**, if you use the [Keboola CLI](/cli/): + + ```bash + kbagent data-app runs --project --app-id + kbagent data-app logs --project --app-id + ``` + + The first lists recent start attempts with their outcome; the second tails the container log. +- **Kai**, for an app it built: ask it why the app isn't running. It reads the same logs. + +## The app doesn't start + +| What you see | Why | Fix | +|---|---|---| +| **Data App is not running** right after a deploy, and the run lasted about a second | The repository lacks the `keboola-config/` folder, or part of it. The runtime log says `App must have keboola-config/nginx/ directory` (or `supervisord/`) and the container exits. This is the single most common start failure. | Add the folder; [what the repository must contain](/data-apps/build-locally/) lists exactly what goes in it. Check with `kbagent data-app validate-repo` before the next deploy. | +| `fatal: Authentication failed` in the job log | Keboola couldn't clone the repository: the access token or SSH key for a private repository is missing, wrong, or expired. | Open **Git Repository** on the app, update **Username** and **Access Token** or the **SSH Private Key**, and redeploy. For a Keboola-managed repository this shouldn't happen; redeploy, and if it repeats, report it with the job ID. | +| The app starts and stops in a loop | The supervisord program can't start the process: a relative path where a full `/app/...` path is needed, a Python command without `uv run`, a `setup.sh` committed without the executable bit, or a `[program:nginx]` entry the image already provides. | Fix the config; the [layout contract](/data-apps/build-locally/) spells out each rule. | +| The install step fails | No `pyproject.toml` or `package.json` where `setup.sh` runs, or `pip install` in `setup.sh`; the image blocks bare `pip` (`externally-managed-environment`). | Commit the manifest; use `uv sync` or `npm install --omit=dev`. | +| The deploy job fails on a package conflict | Two dependencies want incompatible versions, or an unpinned package moved. | Pin versions. Streamlit apps: [Lock package versions](/data-apps/streamlit/lock-version/). | + +## The app runs but misbehaves + +| What you see | Why | Fix | +|---|---|---| +| `Cannot POST /`, "Method Not Allowed", or a blank page on first open | Keboola checks the app with a POST to `/`, and the root route only accepts GET. | Accept POST on `/`: Flask `methods=["GET", "POST"]`, Express `app.all('/')`. Streamlit handles it on its own. | +| Works locally, not in Keboola | The port in nginx's `proxy_pass` doesn't match the port the app listens on, or the app binds an interface nginx can't reach. | Make the ports match and listen on `127.0.0.1` or all interfaces. | +| A streaming response arrives all at once | nginx buffers the whole response before forwarding it. | `proxy_buffering off; proxy_cache off; proxy_request_buffering off;` in that `location` block. | +| The page loads but never updates | The WebSocket handshake isn't upgraded. | Add the WebSocket upgrade directives to that `location` block; the app-building skill's nginx snippet has them. | +| An environment variable is undefined | The secret isn't in the app's configuration, or you're using the wrong name. | Add it; `#my-key` becomes `MY_KEY`. See [Secrets](/data-apps/reference/#secrets). | +| A bundled frontend serves stale or missing files | The build output isn't committed. | Build locally and commit the output; Keboola installs dependencies, it doesn't run your build. | +| A Streamlit app shows an error in the browser, but the logs are empty | Streamlit renders uncaught exceptions in the page and doesn't write them to stderr. | Wrap your entry point so exceptions are logged to stderr before Streamlit shows them. | + +## Sign-in problems + +- **Basic (Password):** the password is on the app's configuration page next to **Open App** once the app is deployed. +- **OIDC:** each provider tab on [Authentication](/data-apps/authentication/) ends with an "If sign-in fails" list for that provider's error texts, redirect-URI mismatches, and audience settings. +- **Nobody can get in after you changed the authentication settings:** the change takes effect on the next start. Click **Redeploy App** (running app) or **Start App** (stopped app). + +## Sleeping and waking + +An idle app suspends after its inactivity timeout. The next visit wakes it and shows a **waking up** page for a moment; if waking fails, a **wakeup error** page appears with a **Show More** link to the reason. Details and the timeout setting: [Sleep and resume](/data-apps/reference/#sleep-and-resume). + +--- + +**Next:** [Apps reference →](/data-apps/reference/) diff --git a/src/content/docs/data-apps/what-are-apps.md b/src/content/docs/data-apps/what-are-apps.md index a182eafac..cb78bd22b 100644 --- a/src/content/docs/data-apps/what-are-apps.md +++ b/src/content/docs/data-apps/what-are-apps.md @@ -38,9 +38,24 @@ How development actually works — code, configuration, data access, deployment Have an existing **Streamlit** app? Streamlit remains supported; its documentation lives in the [Streamlit apps](/data-apps/streamlit/) section. -## Where Kai fits +## Two ways to run an app -Kai can build, edit, and run apps for you. Throughout these docs, "build with Kai" is the recommended path for most users — it removes the low-level setup you'd otherwise do by hand. Developers who want full control can still build locally; see [Build locally](/data-apps/build-locally/). +Every app runs from a Git repository. Where that repository lives decides how you work on the app, so pick the path when you create it. + +**Kai and a Keboola-managed repository.** When Kai creates an app, Keboola also creates a private Git repository for it on your stack (`git..keboola.com/keboola/app-.git`) and Kai commits to it. That's what makes the rest of the Kai experience possible: the live preview, drafts you refine before publishing, and **Modify with Kai** later on. You never touch Git; you describe changes and approve them. This is the recommended path for most people, and the one [Build your first app with Kai](/data-apps/getting-started/) walks through. + +**Your own repository.** Point the app at a repository you host, on GitHub, GitLab, or any Git server, and Keboola clones it on every deploy. You develop wherever you like, with your own editor, your own review process, and coding assistants such as Claude Code using the [app-building skill](/data-apps/build-locally/#develop-with-an-ai-coding-tool). Kai can't edit code it doesn't host, so the Kai builder and its live preview aren't part of this path; you push a change and click **Redeploy**. Your repository has to follow a small [layout contract](/data-apps/build-locally/) so Keboola knows how to start it. [Build locally](/data-apps/build-locally/) covers it end to end, including the terminal path with `kbagent`. + +Everything after the code is the same on both paths: the same runtime, the same [authentication](/data-apps/authentication/) options, the same App URL, the same deploy, redeploy, sleep, and logs. + +| | Kai and a managed repository | Your own repository | +|---|---|---| +| Who writes the code | Kai, from your description | You, or your coding assistant | +| Where it lives | A private repo on your Keboola stack | GitHub, GitLab, or any Git server | +| Changing the app | Tell Kai, preview the draft, publish | Push, then **Redeploy** | +| Live preview and drafts | Yes | No | +| Layout rules for the repo | Handled by Kai | The `keboola-config/` contract | +| Best for | Dashboards, internal tools, getting something in front of people fast | Existing code, teams with a review process, custom stacks | --- diff --git a/src/sidebar.mjs b/src/sidebar.mjs index fcb0b4ce0..e15bd4ede 100644 --- a/src/sidebar.mjs +++ b/src/sidebar.mjs @@ -112,6 +112,8 @@ export const sidebar = [ items: [ { slug: "data-apps/authentication" }, { slug: "data-apps/publish-and-share" }, + { slug: "data-apps/operate" }, + { slug: "data-apps/troubleshooting" }, ], }, { slug: "data-apps/reference" }, From a28495775d4bc522e8a4ae4af3d5eefe8274e673 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 22 Sep 2026 20:37:27 +0200 Subject: [PATCH 03/15] AJDA-3380: fact-checker follow-ups on the Google tab - Drop the unverified claim that Kai preserves the OIDC settings; only the MCP server's default path is known to. - Show the Overview-tab screenshot that actually has the tab bar and the App URL block in view. - Publishing gates the app name as well as the logo. - The propagation note covers audience changes again, not only the redirect URI. - The fresh-window advice names the real reason: the proxy's own session cookie, not a skipped Google sign-in (the proxy sends prompt=select_account). Co-Authored-By: Claude Fable 5.1 --- src/content/docs/data-apps/authentication.mdx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/content/docs/data-apps/authentication.mdx b/src/content/docs/data-apps/authentication.mdx index 1aa001141..a69f6432f 100644 --- a/src/content/docs/data-apps/authentication.mdx +++ b/src/content/docs/data-apps/authentication.mdx @@ -47,7 +47,7 @@ The flow is the same everywhere: open the app in Keboola, register it with your Your provider needs the app's callback URL, so start in Keboola. -1. In your Keboola project, open **Apps** and click the app. It doesn't matter whether Kai built it or you created it yourself, and Kai keeps these settings when it changes the app later. No app yet? [Create one manually](/data-apps/getting-started/#create-an-app-manually) first; it opens on its configuration page. +1. In your Keboola project, open **Apps** and click the app. It doesn't matter whether Kai built it or you created it yourself. No app yet? [Create one manually](/data-apps/getting-started/#create-an-app-manually) first; it opens on its configuration page. 2. Scroll to the **App URL** block. It shows the app's host as a URL prefix plus a generated part, for example `toy-store-sales` and `-74016144.hub.europe-west3.gcp.keboola.com`. Your callback URL is `https://`, that whole host, and `/_proxy/callback`: ``` @@ -60,7 +60,7 @@ Your provider needs the app's callback URL, so start in Keboola. The block sits on the app's **Overview** tab, below **Authentication**: -![The app's configuration page with the App URL block: the URL prefix, the generated host, and a copy button](/data-apps/publish-config.png) +![The app's Overview tab: Description, Authentication, and Git Repository cards, then the App URL block, with the App Info panel on the right](/data-apps/build-in-ui-config.png) Keep this tab open. You now have the callback URL; each app has its own, so register every app with your provider separately. @@ -78,7 +78,7 @@ Pick your provider: 1. If the page shows **Get started**, the project has no consent screen yet. Click it and fill in the wizard: **App name** and **User support email**, the **Audience** (**Internal** or **External**, see above), a contact email, then **Create**. If you see **Branding**, **Audience**, and **Clients** in the left menu instead, the consent screen already exists; carry on. 2. Open [**Branding**](https://console.cloud.google.com/auth/branding). Under **Authorized domains**, add `keboola.com`, the domain your App URL belongs to, and save. Without it, Google refuses the redirect URI in the next step. - 3. Check [**Audience**](https://console.cloud.google.com/auth/audience): **Internal** for your organization only, **External** for any Google account. Leave the **Test users** list and **Publish app** alone. For the sign-in scopes Keboola uses, Google applies neither: an External app lets any Google account in while still in Testing. Publishing only matters if you want your logo on the consent screen, and then Google asks for branding details and a verification review. + 3. Check [**Audience**](https://console.cloud.google.com/auth/audience): **Internal** for your organization only, **External** for any Google account. Leave the **Test users** list and **Publish app** alone. For the sign-in scopes Keboola uses, Google applies neither: an External app lets any Google account in while still in Testing. Publishing only matters if you want your app name and logo shown on the consent screen, and then Google asks for branding details and a verification review. You now have: a consent screen with `keboola.com` authorized and the audience you want. @@ -95,10 +95,12 @@ Pick your provider: **If sign-in fails:** - - **Access blocked: This app's request is invalid**, with `Error 400: redirect_uri_mismatch` — the URI in the OAuth client differs from the app's callback URL. Open **Request details** on that page: it shows the `redirect_uri` Google received. Compare it with the **App URL** block character by character (a stray space or a missing character is the usual cause) and fix the client. No redeploy needed; Google says such changes take from a few minutes to a few hours to apply. + - **Access blocked: This app's request is invalid**, with `Error 400: redirect_uri_mismatch` — the URI in the OAuth client differs from the app's callback URL. Open **Request details** on that page: it shows the `redirect_uri` Google received. Compare it with the **App URL** block character by character (a stray space or a missing character is the usual cause) and fix the client. - Someone outside your organization can't sign in — expected with the Internal audience. Switch to External on the **Audience** page if that's not what you want. - Someone outside your organization *can* sign in — the audience is External. For the scopes Keboola uses, Google doesn't limit an External app to its test users, even in Testing. Switch to Internal. - `invalid_client`, or a token error right after signing in — the Client ID or Client secret in Keboola doesn't match the OAuth client. Paste them again, or add a new secret in Google Cloud and update the app. + + Changed the redirect URI or the audience on Google's side later? No redeploy needed; Google says such changes take from a few minutes to a few hours to apply. Let people sign in with their Microsoft work account. @@ -194,7 +196,7 @@ Pick your provider: 1. A new app: set its code source and click **Deploy App**; the short wizard asks for the backend size and an inactivity timeout (details: [Create an app manually](/data-apps/getting-started/#create-an-app-manually)). Just testing sign-in? A **Streamlit** app with a one-line inline script is the quickest thing to deploy. An app that's already running needs **Redeploy App** for the new authentication to take effect; a stopped one, **Start App**. 2. When the status turns **Active**, click **Open App**. Your provider asks you to sign in, then sends you into the app. -3. Test with the accounts that matter, each in a fresh private window: one that should get in, one that shouldn't. A browser that's already signed in to the provider skips the sign-in screen and hides the result. +3. Test with the accounts that matter, each in a fresh private window: one that should get in, one that shouldn't. Once you're in, the app's own session cookie keeps you signed in, so a second try in the same window proves nothing. ### Provider settings at a glance From 787d46ca690ba6a50b7a983a56d9feae26493fe2 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 22 Sep 2026 20:45:07 +0200 Subject: [PATCH 04/15] AJDA-3380: say when OIDC is not needed, date the provider steps, state what each build path needs Three gaps left after the Google-tab and two-paths work, all from the launch call: a reader must know whether a hard path is even theirs before they start, and third-party steps are ours to keep correct. * The OIDC section opens with the question instead of the procedure. Only company-account sign-in justifies it; password, GitHub, GitLab and JumpCloud need nothing outside Keboola. OIDC is the only method that needs admin rights in someone else's console, and the note says so. * Each provider tab now carries the date its console steps were last walked through: 2026-09-22 for Google, 2026-09-14 for Entra ID, Okta and Auth0, with a warning on the three older ones that menu names drift. Honest dates, not a blanket claim. * "Two ways to build" says what each path costs before the reader picks: a project and nothing else for Kai, a Git account and about half an hour the first time for the local path. Build clean, 369 pages, broken internal links 0, missing images 0. The four Google console deep links added earlier all resolve. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/authentication.mdx | 8 ++++++++ src/content/docs/data-apps/index.md | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/content/docs/data-apps/authentication.mdx b/src/content/docs/data-apps/authentication.mdx index a69f6432f..789114ad1 100644 --- a/src/content/docs/data-apps/authentication.mdx +++ b/src/content/docs/data-apps/authentication.mdx @@ -37,6 +37,10 @@ Once an app is deployed, its URL is publicly available. Protect it so only the r ## OIDC (single sign-on) +:::note[Do you actually need OIDC?] +Only set this up if people must sign in with their **company accounts** — so that access follows your directory, and someone who leaves the company loses it. Everything else is cheaper: **Basic (Password)** shares one password and needs nothing outside Keboola, and **GitHub**, **GitLab** or **JumpCloud** restrict access by organization or team with far less setup. OIDC is the only method that needs admin rights in a console outside Keboola. +::: + OIDC lets users log into your app through your single sign-on (SSO) provider. Keboola has ready-made provider options for Google (**Google SSO**), Microsoft Entra ID (**Azure OIDC**), **Okta**, and **Auth0**, plus **Generic OIDC** for any other OpenID Connect provider. Users sign in with the provider you configured; if an app has more than one provider, they first pick an **Authentication Provider**. ![The app's sign-in page asking the user to select an authentication provider, one button per configured provider](/data-apps/auth-select-oidc-provider.png) @@ -101,6 +105,7 @@ Pick your provider: - `invalid_client`, or a token error right after signing in — the Client ID or Client secret in Keboola doesn't match the OAuth client. Paste them again, or add a new secret in Google Cloud and update the app. Changed the redirect URI or the audience on Google's side later? No redeploy needed; Google says such changes take from a few minutes to a few hours to apply. + *Console steps walked through on 2026-09-22.* Let people sign in with their Microsoft work account. @@ -132,6 +137,7 @@ Pick your provider: - `AADSTS50011: The redirect URI … does not match` — the redirect URI in the registration differs from the app's callback URL. Fix it under **Manage → Authentication**. - `AADSTS7000215: Invalid client secret provided` — you pasted the secret's **Secret ID** instead of its **Value**, or the secret expired. Create a new one and update the app. - `AADSTS50105: … is not assigned to a role for the application` — you required assignment and this user isn't assigned. Add them under **Enterprise apps → your app → Users and groups**. + *Console steps last walked through on 2026-09-14; the Entra admin center changes often, so check the menu names as you go.* Let people sign in with their Okta account. @@ -156,6 +162,7 @@ Pick your provider: - "The 'redirect_uri' parameter must be a Login redirect URI in the client app settings" — the URI in the integration differs from the app's callback URL. Fix **Sign-in redirect URIs** on the integration's **General** tab. - "User is not assigned to the client application" — the user, or their group, isn't assigned to the integration. Add them on the **Assignments** tab. - Issuer or discovery error when the app starts the sign-in — check **Domain/Org URL**: `https://`, your Okta domain, `/oauth2/default`, no trailing slash. If your org has no `default` authorization server, use `https://` instead. + *Console steps last walked through on 2026-09-14; check the menu names as you go.* Let people sign in through Auth0, with whatever connections your tenant offers: username and password, social logins, or enterprise identity providers. @@ -179,6 +186,7 @@ Pick your provider: - "Callback URL mismatch" on an Auth0 error page — the URL in **Allowed Callback URLs** differs from the app's callback URL. Fix it under **Settings → Application URIs**. - Issuer mismatch or discovery error when the app starts the sign-in — the **Issuer URL** must match the `issuer` in Auth0's discovery document character for character: `https://`, your Auth0 domain, trailing slash. - Users of one connection can't sign in — that connection isn't enabled for this application. Turn it on under the application's **Connections** tab. + *Console steps last walked through on 2026-09-14; check the menu names as you go.* Any OpenID Connect provider works through the **Generic OIDC** option. diff --git a/src/content/docs/data-apps/index.md b/src/content/docs/data-apps/index.md index 0372b5744..eddab5dec 100644 --- a/src/content/docs/data-apps/index.md +++ b/src/content/docs/data-apps/index.md @@ -23,8 +23,8 @@ Dashboards and reports, internal tools, data narratives, configurators, and agen ## Two ways to build -- **[Build with Kai](/data-apps/getting-started/)** — describe the app in plain language and Kai reads your data, builds a live draft you preview and refine, and publishes when you're ready. No coding needed — the walkthrough takes about 10 minutes. The same **Create App** screen also offers [manual creation](/data-apps/getting-started/#create-an-app-manually) if you'd rather configure the app yourself. -- **[Build locally](/data-apps/build-locally/)** — develop with your own tools and Git account, use the [AI Kit](/ai/ai-kit/) coding-assistant plugins, and sync the app to your project. For developers who want full control — that page covers how Python/JS development works day to day. +- **[Build with Kai](/data-apps/getting-started/)** — describe the app in plain language and Kai reads your data, builds a live draft you preview and refine, and publishes when you're ready. No coding needed — the walkthrough takes about 10 minutes. The same **Create App** screen also offers [manual creation](/data-apps/getting-started/#create-an-app-manually) if you'd rather configure the app yourself. **You need:** a Keboola project. Nothing else. +- **[Build locally](/data-apps/build-locally/)** — develop with your own tools and Git account, use the [AI Kit](/ai/ai-kit/) coding-assistant plugins, and sync the app to your project. For developers who want full control — that page covers how Python/JS development works day to day. **You need:** a Git account and somewhere to host the repository; about half an hour the first time. The difference underneath is where the code lives: a repository Keboola manages for Kai, or one you host yourself. [Two ways to run an app](/data-apps/what-are-apps/#two-ways-to-run-an-app) explains what each gives you. From 7cbabd24775b0876d6cd4d08451e9c46a365df14 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 22 Sep 2026 20:54:55 +0200 Subject: [PATCH 05/15] PRDCT-692: fold in the guide-tester findings, starting with the one against my own note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cold-read of the OIDC path found no blocker but six majors. Five are fixed here; the sixth needs a console. * **The entry note was wrong.** It claimed OIDC is the only method needing admin rights outside Keboola. GitHub sends you to Developer Settings, GitLab to Settings > Applications, JumpCloud to its Admin Console. A reader taking the note at face value would pick GitHub to dodge "the hard one" and spend the same effort. Now: Basic is the only method needing nothing outside Keboola; the other three need an OAuth app but no consent screen and no audience decision. * **Step 1 invited the page's own most common failure.** It said "copy its callback URL", but the copy button in the App URL block yields the app URL. Copy, paste into Authorized redirect URIs, and you get the redirect_uri_mismatch this page lists first. The step now names the button, says what it gives you, and says to append /_proxy/callback. * **Google SSO cannot restrict below the whole audience.** Entra has assignment, Okta has assignments, Google has neither, and the page did not admit it. Someone could ship an internal-finance app to all 800 people in the org believing it was scoped. Stated in the Google tab, with the alternatives. * **The negative test had no pass signature.** A blocked outside account looks like a fault, and the page's only nearby lever was "switch to External" — which turns a correctly closed app into a public one. Both signatures are now spelled out, with the trap called out. * **One URL, two placeholder vocabularies.** GitHub, GitLab and JumpCloud used `` three times before it was defined at the bottom, and it does not mean the App ID the screenshot shows. All call sites now use Step 1's spelling. * Time estimate was "a few minutes" against a measured 40; now about 15, longer with no consent screen. The scopes the Internal/External decision rests on are named (openid, email, profile). Left for a human: whether Google shows **Authorized domains** on an Internal-audience project. Hedged in the step and flagged TODO(human-review) in an MDX comment, which does not ship to the built HTML (verified: 0 occurrences in dist). Build clean, 369 pages, broken internal links 0, missing images 0. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/authentication.mdx | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/content/docs/data-apps/authentication.mdx b/src/content/docs/data-apps/authentication.mdx index 789114ad1..72d98f3ff 100644 --- a/src/content/docs/data-apps/authentication.mdx +++ b/src/content/docs/data-apps/authentication.mdx @@ -38,21 +38,21 @@ Once an app is deployed, its URL is publicly available. Protect it so only the r ## OIDC (single sign-on) :::note[Do you actually need OIDC?] -Only set this up if people must sign in with their **company accounts** — so that access follows your directory, and someone who leaves the company loses it. Everything else is cheaper: **Basic (Password)** shares one password and needs nothing outside Keboola, and **GitHub**, **GitLab** or **JumpCloud** restrict access by organization or team with far less setup. OIDC is the only method that needs admin rights in a console outside Keboola. +Only set this up if people must sign in with their **company accounts** — so that access follows your directory, and someone who leaves the company loses it. **Basic (Password)** is the only method that needs nothing outside Keboola: it shares one password and you're done. **GitHub**, **GitLab** and **JumpCloud** also restrict access by organization or team, and they still need an OAuth app in that provider's console, but no consent screen and no audience decision — noticeably less work than OIDC. ::: OIDC lets users log into your app through your single sign-on (SSO) provider. Keboola has ready-made provider options for Google (**Google SSO**), Microsoft Entra ID (**Azure OIDC**), **Okta**, and **Auth0**, plus **Generic OIDC** for any other OpenID Connect provider. Users sign in with the provider you configured; if an app has more than one provider, they first pick an **Authentication Provider**. ![The app's sign-in page asking the user to select an authentication provider, one button per configured provider](/data-apps/auth-select-oidc-provider.png) -The flow is the same everywhere: open the app in Keboola, register it with your provider using the app's callback URL, paste the provider's credentials into the app's **Authentication** settings, and deploy. It's a few minutes of clicking in two browser tabs, and it needs admin rights in the provider's console: for Google, the **Owner** or **OAuth Config Editor** role on a Google Cloud project; for the others, the right to register applications in your tenant. Kai and kbagent can't do this part for you. +The flow is the same everywhere: open the app in Keboola, register it with your provider using the app's callback URL, paste the provider's credentials into the app's **Authentication** settings, and deploy. Expect about 15 minutes of clicking in two browser tabs, and longer the first time in a Google Cloud project that has no consent screen yet. It needs admin rights in the provider's console: for Google, the **Owner** or **OAuth Config Editor** role on a Google Cloud project; for the others, the right to register applications in your tenant. Kai and kbagent can't do this part for you. ### Step 1 — Open the app and copy its callback URL Your provider needs the app's callback URL, so start in Keboola. 1. In your Keboola project, open **Apps** and click the app. It doesn't matter whether Kai built it or you created it yourself. No app yet? [Create one manually](/data-apps/getting-started/#create-an-app-manually) first; it opens on its configuration page. -2. Scroll to the **App URL** block. It shows the app's host as a URL prefix plus a generated part, for example `toy-store-sales` and `-74016144.hub.europe-west3.gcp.keboola.com`. Your callback URL is `https://`, that whole host, and `/_proxy/callback`: +2. On the app's **Overview** tab, scroll to the **App URL** block. It shows the app's host as a URL prefix plus a generated part, for example `toy-store-sales` and `-74016144.hub.europe-west3.gcp.keboola.com`. Click the block's copy button — it gives you the app's URL, **not** the callback URL. Paste it where you need it and add `/_proxy/callback` to the end: ``` https://-.hub./_proxy/callback @@ -62,7 +62,7 @@ Your provider needs the app's callback URL, so start in Keboola. The block is there from the moment the app exists; you don't have to deploy first. -The block sits on the app's **Overview** tab, below **Authentication**: +The block sits below **Authentication**: ![The app's Overview tab: Description, Authentication, and Git Repository cards, then the App URL block, with the App Info panel on the right](/data-apps/build-in-ui-config.png) @@ -76,14 +76,17 @@ Pick your provider: Let people sign in with their Google account. - **Before you start.** The OAuth client lives in a Google Cloud project, not in the Google Workspace admin console, so you need a project where you can manage the consent screen and OAuth clients: the **Owner** role, or the **OAuth Config Editor** role. Decide who should get in, too. With the sign-in scopes Keboola requests, an **Internal** audience limits sign-in to your Google Workspace organization (the project must belong to that organization), and an **External** audience lets in anyone with a Google account. + **Before you start.** The OAuth client lives in a Google Cloud project, not in the Google Workspace admin console, so you need a project where you can manage the consent screen and OAuth clients: the **Owner** role, or the **OAuth Config Editor** role. Decide who should get in, too. With the sign-in scopes Keboola requests (`openid`, `email`, `profile`), an **Internal** audience limits sign-in to your Google Workspace organization (the project must belong to that organization), and an **External** audience lets in anyone with a Google account. **Set up the consent screen.** Google keeps it under **Google Auth Platform**; open it directly at [console.cloud.google.com/auth/overview](https://console.cloud.google.com/auth/overview) and pick your project. 1. If the page shows **Get started**, the project has no consent screen yet. Click it and fill in the wizard: **App name** and **User support email**, the **Audience** (**Internal** or **External**, see above), a contact email, then **Create**. If you see **Branding**, **Audience**, and **Clients** in the left menu instead, the consent screen already exists; carry on. - 2. Open [**Branding**](https://console.cloud.google.com/auth/branding). Under **Authorized domains**, add `keboola.com`, the domain your App URL belongs to, and save. Without it, Google refuses the redirect URI in the next step. + 2. Open [**Branding**](https://console.cloud.google.com/auth/branding). Under **Authorized domains**, add `keboola.com`, the domain your App URL belongs to, and save. Without it, Google refuses the redirect URI in the next step. (Google shows **Authorized domains** on projects with an **External** audience; on an **Internal** one it may restrict domains for you — if you don't see the field, carry on to the next step.) + {/* TODO(human-review, Nikita): open Branding on an Internal-audience project and confirm whether Authorized domains is shown. If it is always there, drop the parenthesis. guide-tester MAJOR 4, 2026-09-22. */} 3. Check [**Audience**](https://console.cloud.google.com/auth/audience): **Internal** for your organization only, **External** for any Google account. Leave the **Test users** list and **Publish app** alone. For the sign-in scopes Keboola uses, Google applies neither: an External app lets any Google account in while still in Testing. Publishing only matters if you want your app name and logo shown on the consent screen, and then Google asks for branding details and a verification review. + 4. Know what the audience buys you: it is the only access control Google SSO offers here. Anyone in the audience you chose can open the app — there is no group or user filter, and Keboola takes only a Client ID and secret for this provider. To narrow it further, use Microsoft Entra ID or Okta, which do have assignment, or check the signed-in user inside the app itself. + You now have: a consent screen with `keboola.com` authorized and the audience you want. **Create the OAuth client:** @@ -206,6 +209,8 @@ Pick your provider: 2. When the status turns **Active**, click **Open App**. Your provider asks you to sign in, then sends you into the app. 3. Test with the accounts that matter, each in a fresh private window: one that should get in, one that shouldn't. Once you're in, the app's own session cookie keeps you signed in, so a second try in the same window proves nothing. + Know what a correct rejection looks like before you read it as a fault. With Google and an **Internal** audience, an account from outside your organization is stopped by Google with a message that the app is restricted to its organization — that is the app working. Do **not** switch the audience to **External** to make that message go away; it opens the app to every Google account there is. The reverse is the quiet failure: if an outside account lands in the app, your audience is External and the app is public to anyone signed in to Google. + ### Provider settings at a glance | Provider | Keboola provider option | What you enter besides Client ID and Client secret | @@ -243,7 +248,7 @@ Restrict access to your app using GitHub OAuth. Users authenticate via their Git ### Setup instructions 1. Go to your GitHub account **Settings > Developer Settings > OAuth Apps** and create a new OAuth App. -2. Set the **Authorization callback URL** to: `https://.hub./_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). +2. Set the **Authorization callback URL** to: the app's callback URL — `https://-.hub./_proxy/callback`, the whole host from the **App URL** block plus `/_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). 3. Copy the **Client ID** and **Client Secret** from the created OAuth App. 4. In your Keboola app configuration, select **GitHub** as the authentication method. 5. Paste the **Client ID** and **Client Secret**. @@ -274,7 +279,7 @@ Restrict access to your app using GitLab OAuth. Users authenticate via their Git ### Setup instructions 1. Go to your GitLab instance **Settings > Applications** and create a new application. -2. Set the **Redirect URI** to: `https://.hub./_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). +2. Set the **Redirect URI** to: the app's callback URL — `https://-.hub./_proxy/callback`, the whole host from the **App URL** block plus `/_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). 3. Ensure the `openid`, `profile`, and `email` scopes are selected. If you use group or project restrictions, also select `read_api`. 4. Copy the **Application ID** and **Secret**. 5. In your Keboola app configuration, select **GitLab** as the authentication method. @@ -305,7 +310,7 @@ Restrict access to your app using JumpCloud OIDC. Users authenticate via their J 1. In the **JumpCloud Admin Console**, go to **SSO** and create a new application (or use an existing one). 2. Configure the application as an **OIDC** application. -3. Set the **Redirect URI** to: `https://.hub./_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). +3. Set the **Redirect URI** to: the app's callback URL — `https://-.hub./_proxy/callback`, the whole host from the **App URL** block plus `/_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). 4. Copy the **Client ID** and **Client Secret** from the SSO tab. 5. In your Keboola app configuration, select **JumpCloud** as the authentication method. 6. Paste the **Client ID**, **Client Secret**, **Issuer URL**, and **Logout URL**. @@ -317,12 +322,12 @@ Restrict access to your app using JumpCloud OIDC. Users authenticate via their J All authentication methods that use OAuth or OIDC require a callback URL. The format is always: ``` -https://.hub./_proxy/callback +https://-.hub./_proxy/callback ``` For example: `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback` -`` stands for the whole host shown in the **App URL** block on the app's configuration page: the URL prefix, a hyphen, and the App ID (for example `toy-store-sales-74016144`). Take that host, add `https://` in front and `/_proxy/callback` at the end. +`-.hub.` is the whole host shown in the **App URL** block on the app's configuration page: the URL prefix, a hyphen, and the App ID ( --- From 0addba24b78d59601febc18311add8756a48e326 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 22 Sep 2026 21:38:42 +0200 Subject: [PATCH 06/15] AJDA-3380: corrections from the live Google run Walked the whole Google tab against a real Google Cloud project (keboola-docs-test) and a real app in project 264. What the run changed: - The central claim is now proven, not inferred: the sign-in request the proxy sends carries scope=openid+email+profile, and an External app in Testing status with an empty test-user list let the account straight in. Google's own Audience page says the opposite, so the guide now names that text and says it does not apply here. - Authorized domains are no longer a prerequisite: the client form states that domains from the redirect URI are added to the consent screen automatically. That step is now optional. - The Get started branch was wrong: Branding, Audience and Clients are in the left menu even on an unconfigured project, so they cannot be the signal. The content area is. - The Audience page shows publishing status, user cap and test users only for an External app. - The secret field is masked after saving; the encrypted KBC::ProjectSecureGKMS:: value appears only behind the eye icon. - The deploy wizard asks for three things, not two. - The consent screen appears on first sign-in for Internal apps too, and the app page can still read stopped until reloaded. - Added /_proxy/sign_out as the way to end the app session between tests. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/authentication.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/content/docs/data-apps/authentication.mdx b/src/content/docs/data-apps/authentication.mdx index a69f6432f..1dba31eae 100644 --- a/src/content/docs/data-apps/authentication.mdx +++ b/src/content/docs/data-apps/authentication.mdx @@ -76,9 +76,9 @@ Pick your provider: **Set up the consent screen.** Google keeps it under **Google Auth Platform**; open it directly at [console.cloud.google.com/auth/overview](https://console.cloud.google.com/auth/overview) and pick your project. - 1. If the page shows **Get started**, the project has no consent screen yet. Click it and fill in the wizard: **App name** and **User support email**, the **Audience** (**Internal** or **External**, see above), a contact email, then **Create**. If you see **Branding**, **Audience**, and **Clients** in the left menu instead, the consent screen already exists; carry on. - 2. Open [**Branding**](https://console.cloud.google.com/auth/branding). Under **Authorized domains**, add `keboola.com`, the domain your App URL belongs to, and save. Without it, Google refuses the redirect URI in the next step. - 3. Check [**Audience**](https://console.cloud.google.com/auth/audience): **Internal** for your organization only, **External** for any Google account. Leave the **Test users** list and **Publish app** alone. For the sign-in scopes Keboola uses, Google applies neither: an External app lets any Google account in while still in Testing. Publishing only matters if you want your app name and logo shown on the consent screen, and then Google asks for branding details and a verification review. + 1. If the page says **Google Auth Platform not configured yet** and offers **Get started**, the project has no consent screen yet. Click it and fill in the wizard: **App name** and **User support email**, the **Audience** (**Internal** or **External**, see above), a contact email, then tick the User Data Policy box and click **Create**. If the page shows your app's details instead, the consent screen already exists; carry on. (**Branding**, **Audience** and **Clients** sit in the left menu either way, so they don't tell you which case you're in.) + 2. Optional: open [**Branding**](https://console.cloud.google.com/auth/branding) and add `keboola.com` under **Authorized domains**. Google adds it for you when you save the redirect URI in the next step ("The domains of the URIs you add below will be automatically added to your OAuth consent screen as authorized domains"), so this is only worth doing if you want it in place beforehand. + 3. Check [**Audience**](https://console.cloud.google.com/auth/audience). An **Internal** app shows only its user type; an **External** one also shows **Publishing status**, an **OAuth user cap** and a **Test users** list. Ignore all three. Google's own text there says that while the status is Testing "only test users are able to access the app", and that is not true for a Keboola app: the proxy asks for the `openid`, `email` and `profile` scopes only, and Google exempts exactly that set from the test-user rule, so an External app lets any Google account in whether or not you publish it. Publishing only matters if you want your app name and logo shown on the consent screen, and then Google asks for branding details and a verification review. You now have: a consent screen with `keboola.com` authorized and the audience you want. @@ -91,7 +91,7 @@ Pick your provider: You now have: a Client ID and a Client secret. - **Back in Keboola.** On the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Google SSO** in the **Provider** dropdown, paste the **Client ID** and **Client secret** (this option has no issuer field), and click **Save**. After saving, the secret field shows an encrypted value starting with `KBC::ProjectSecureGKMS::`. That's your secret stored encrypted, not a replacement; to change it later, paste a new value over it. + **Back in Keboola.** On the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Google SSO** in the **Provider** dropdown, paste the **Client ID** and **Client secret** (this option has no issuer field), and click **Save**. After saving, the secret field is masked. Reveal it with the eye icon and you'll see an encrypted value starting with `KBC::ProjectSecureGKMS::` rather than what you pasted — that's your secret stored encrypted, not a replacement. To change it later, paste a new value over it. **If sign-in fails:** @@ -194,9 +194,9 @@ Pick your provider: ### Step 3 — Deploy and test -1. A new app: set its code source and click **Deploy App**; the short wizard asks for the backend size and an inactivity timeout (details: [Create an app manually](/data-apps/getting-started/#create-an-app-manually)). Just testing sign-in? A **Streamlit** app with a one-line inline script is the quickest thing to deploy. An app that's already running needs **Redeploy App** for the new authentication to take effect; a stopped one, **Start App**. -2. When the status turns **Active**, click **Open App**. Your provider asks you to sign in, then sends you into the app. -3. Test with the accounts that matter, each in a fresh private window: one that should get in, one that shouldn't. Once you're in, the app's own session cookie keeps you signed in, so a second try in the same window proves nothing. +1. A new app: set its code source and click **Deploy App**; the short wizard asks for the backend version, the backend size and an inactivity timeout (details: [Create an app manually](/data-apps/getting-started/#create-an-app-manually)). Just testing sign-in? A **Streamlit** app with a one-line inline script is the quickest thing to deploy. An app that's already running needs **Redeploy App** for the new authentication to take effect; a stopped one, **Start App**. +2. When the status turns **Active**, click **Open App**. Your provider asks you to sign in and, the first time, to allow the app to see your name and email address; then it sends you into the app. If the page still says the app is stopped right after a deploy, reload it — the status catches up a moment later. +3. Test with the accounts that matter: one that should get in, one that shouldn't. Once you're in, the app's own session cookie keeps you signed in, so a second try in the same window proves nothing — use a fresh private window, or open `https:///_proxy/sign_out` to end the app session and start over. ### Provider settings at a glance From 2bf9a2b19eff21680826810d8d699c073fa41487 Mon Sep 17 00:00:00 2001 From: Nikita Date: Tue, 22 Sep 2026 22:46:35 +0200 Subject: [PATCH 07/15] AJDA-3380: match the app menu labels to the live UI Seen on a real app in project 264 while cleaning up after the OIDC run: the menu reads Suspend app, Copy app, Automate, Debug mode, Delete app, and suspending asks for confirmation. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/operate.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/docs/data-apps/operate.md b/src/content/docs/data-apps/operate.md index caf192659..94674ecfe 100644 --- a/src/content/docs/data-apps/operate.md +++ b/src/content/docs/data-apps/operate.md @@ -8,7 +8,7 @@ Once an app is deployed, everything you do with it starts on its page under **Ap ## Read the app's state -The header shows the app's status and the one or two actions that make sense for it: **Deploy App** for an app that has never run, **Start App** for a stopped one, **Open App** and **Redeploy App** for a running one, and **Modify with Kai** on apps Kai built. The **⋯** menu holds the rest: **Copy app**, **Automate** (add it to a flow), **Debug mode**, **Delete app**. +The header shows the app's status and the one or two actions that make sense for it: **Deploy App** for an app that has never run, **Start App** for a stopped one, **Open App** and **Redeploy App** for a running one, and **Modify with Kai** on apps Kai built. The **⋯** menu holds the rest: **Suspend app**, **Copy app**, **Automate** (add it to a flow), **Debug mode**, **Delete app**. The tabs below the header split the app's life into views: **Overview** (description, authentication, code source, App URL), **Advanced Settings** (environment variables and secrets, theme, data mappings), **All Runs** (every start attempt), **Terminal Logs** (stdout and stderr while it runs), **Versions** (every configuration change), and **Drafts** while Kai has a draft open. The **App Info** panel on the right shows the backend version and size, the auto-sleep timeout, the owner, and the App ID. @@ -28,7 +28,7 @@ Secrets deserve one note: after you save one, the field shows an encrypted value ## Stop, start, sleep - **Sleeping** is automatic. After the inactivity timeout (five minutes to 24 hours, set in the deploy wizard) the app suspends; the next visit wakes it and shows a short **waking up** page. You pay only for time the app is awake or waiting to suspend. -- **Stopping** is deliberate: **⋯ → Suspend App** stops the container and takes the URL offline while keeping the configuration. **Start App** brings it back with the same settings. +- **Stopping** is deliberate: **⋯ → Suspend app** stops the container and takes the URL offline while keeping the configuration. Keboola asks you to confirm. **Start App** brings it back with the same settings. - **Redeploy App** restarts a running app with the current configuration and, for apps on your own repository, the current branch head. Sleep and wake behaviour in detail, including the wakeup error page: [Sleep and resume](/data-apps/reference/#sleep-and-resume). From 3d97aec195bfde262859589af527208106ccd59d Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 23 Sep 2026 10:15:01 +0200 Subject: [PATCH 08/15] Don't name a GCP-only cipher prefix as the one everyone sees The Google tab said the revealed secret starts with KBC::ProjectSecureGKMS::. That prefix is GCP-only: extend/encryption documents KBC::ProjectSecureKV:: on Azure and KBC::ProjectSecure:: on AWS, and the three are not interchangeable. A reader on an AWS stack who didn't see the documented string had no way to tell whether the save had worked. Name the common stem and link the encryption page instead. Found by the fact-checker agent while reviewing the wave-2 Apps pages, which carried the same sentence. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/authentication.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/data-apps/authentication.mdx b/src/content/docs/data-apps/authentication.mdx index 1dba31eae..cc8df744a 100644 --- a/src/content/docs/data-apps/authentication.mdx +++ b/src/content/docs/data-apps/authentication.mdx @@ -91,7 +91,7 @@ Pick your provider: You now have: a Client ID and a Client secret. - **Back in Keboola.** On the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Google SSO** in the **Provider** dropdown, paste the **Client ID** and **Client secret** (this option has no issuer field), and click **Save**. After saving, the secret field is masked. Reveal it with the eye icon and you'll see an encrypted value starting with `KBC::ProjectSecureGKMS::` rather than what you pasted — that's your secret stored encrypted, not a replacement. To change it later, paste a new value over it. + **Back in Keboola.** On the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Google SSO** in the **Provider** dropdown, paste the **Client ID** and **Client secret** (this option has no issuer field), and click **Save**. After saving, the secret field is masked. Reveal it with the eye icon and you'll see an encrypted value starting with `KBC::ProjectSecure` rather than what you pasted. That is your secret stored [encrypted](/extend/encryption/), not a replacement; the rest of the prefix depends on which cloud your stack runs on. To change it later, paste a new value over it. **If sign-in fails:** From 171f18b146da27fa480d6f0953feb1bf55da874c Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 23 Sep 2026 10:18:32 +0200 Subject: [PATCH 09/15] Apps wave 2: fold in the fact-checker and guide-tester findings Both agents ran over the three new pages. Three things they found were wrong rather than unclear: - The cipher prefix KBC::ProjectSecureGKMS:: is GCP-only. Name the common stem and link extend/encryption, so a reader on AWS or Azure can still tell the save worked. - "Every app runs from a Git repository" is false for Streamlit, which the same page keeps in scope four lines earlier. Say so. - A Keboola-managed repository does not require Kai: kbagent data-app create --use-managed-git-repo provisions the same repo empty. The table's "Who writes the code" row had ruled that combination out. The rest were reachability and accuracy: - Two ways to run an app now answers "why is my builder missing" in its first two lines, because that is the question AJDA-3319 will send readers here with, and says the choice is made at creation. - validate-repo is printed with its required --git-repo, and the contract links carry the app-building skill's reference alongside build-locally, which only gains the contract when #1128 lands. - Redeploy no longer claims a gapless swap; the MCP server says the app may report stopped while it restarts. - Versions gains the way back: an app is a component configuration, so rollback works, and a redeploy puts the old settings in service. - Dropped the unsourced App Info "owner" field and the unverified suspend confirmation; settled the backend-size contradiction (wizard, not page); added the Storage Access failure row, the GitHub/GitLab/ JumpCloud sign-in pointer, and terminal equivalents for each task. - Operate and Troubleshooting were orphans: publish-and-share now points at them, as do index and the end of getting-started. - The deploy wizard asks three things, not two (seen live 22.09), so getting-started says three. Its screenshot still shows two and needs reshooting after the UI freeze. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/getting-started.md | 4 ++- src/content/docs/data-apps/index.md | 4 +-- src/content/docs/data-apps/operate.md | 26 +++++++++++++------ .../docs/data-apps/publish-and-share.md | 4 +-- src/content/docs/data-apps/troubleshooting.md | 12 +++++---- src/content/docs/data-apps/what-are-apps.md | 19 +++++++------- 6 files changed, 41 insertions(+), 28 deletions(-) diff --git a/src/content/docs/data-apps/getting-started.md b/src/content/docs/data-apps/getting-started.md index 3a525404a..efbef29d0 100644 --- a/src/content/docs/data-apps/getting-started.md +++ b/src/content/docs/data-apps/getting-started.md @@ -101,12 +101,14 @@ Prefer to set the app up yourself, without the chat? Manual creation lives on th ![The app's configuration page: the Overview tab with Authentication and a Git Repository section, plus the App Info panel showing backend version, size, auto-sleep, and App ID](/data-apps/build-in-ui-config.png) -6. Click **Deploy App**. A short wizard asks for the **backend size** and an **inactivity timeout**, then deploys the app. (The same wizard runs on **Redeploy**.) +6. Click **Deploy App**. A short wizard asks for the **backend version**, the **backend size** and an **inactivity timeout**, then deploys the app. (The same wizard runs on **Redeploy**.) ![The deploy/redeploy wizard: backend size and inactivity timeout](/data-apps/deploy-timeout-backedsize.png) 7. When the status turns **Active**, click **Open App** to open it at its public URL. Use **Redeploy** to apply any later config change; see [App actions](/data-apps/reference/#app-actions). +From here on, the app is something you run rather than something you build: [Operate and update an app](/data-apps/operate/) covers shipping changes, secrets, sleeping and versions. + --- **Next:** [Build an app locally →](/data-apps/build-locally/) diff --git a/src/content/docs/data-apps/index.md b/src/content/docs/data-apps/index.md index 0372b5744..7883394d1 100644 --- a/src/content/docs/data-apps/index.md +++ b/src/content/docs/data-apps/index.md @@ -26,11 +26,11 @@ Dashboards and reports, internal tools, data narratives, configurators, and agen - **[Build with Kai](/data-apps/getting-started/)** — describe the app in plain language and Kai reads your data, builds a live draft you preview and refine, and publishes when you're ready. No coding needed — the walkthrough takes about 10 minutes. The same **Create App** screen also offers [manual creation](/data-apps/getting-started/#create-an-app-manually) if you'd rather configure the app yourself. - **[Build locally](/data-apps/build-locally/)** — develop with your own tools and Git account, use the [AI Kit](/ai/ai-kit/) coding-assistant plugins, and sync the app to your project. For developers who want full control — that page covers how Python/JS development works day to day. -The difference underneath is where the code lives: a repository Keboola manages for Kai, or one you host yourself. [Two ways to run an app](/data-apps/what-are-apps/#two-ways-to-run-an-app) explains what each gives you. +Both routes end up in a Git repository, either one Keboola manages for the app or one you host. [Two ways to run an app](/data-apps/what-are-apps/#two-ways-to-run-an-app) explains what each gives you, including why the Kai builder appears on one and not the other. ## Run and share -Every app has its own settings page in **Apps**: who can open it ([Authentication](/data-apps/authentication/) — public, password, or your company SSO), its URL and how to [publish and share](/data-apps/publish-and-share/) it, and operational settings like backend size and auto-sleep ([Reference](/data-apps/reference/)). Deploying and redeploying is one click; logs are right on the app's detail. +Every app has its own settings page in **Apps**: who can open it ([Authentication](/data-apps/authentication/) — public, password, or your company SSO), its URL and how to [publish and share](/data-apps/publish-and-share/) it, and operational settings like backend size and auto-sleep ([Reference](/data-apps/reference/)). Deploying and redeploying is one click; logs are right on the app's detail. Once it's live, [Operate and update an app](/data-apps/operate/) covers the day-to-day work and [Troubleshooting](/data-apps/troubleshooting/) covers the days it goes wrong. ## Beyond dashboards diff --git a/src/content/docs/data-apps/operate.md b/src/content/docs/data-apps/operate.md index 94674ecfe..a8f1c2017 100644 --- a/src/content/docs/data-apps/operate.md +++ b/src/content/docs/data-apps/operate.md @@ -6,42 +6,52 @@ description: "Day-to-day work on a deployed Keboola app: read its state, ship a Once an app is deployed, everything you do with it starts on its page under **Apps**. This page walks through the routine tasks; [Apps reference](/data-apps/reference/) has the full list of settings and actions. +Every task here can also be done from a terminal with `kbagent`. Those commands all take `--project `, the name you gave the project when you [connected it](/cli/concepts/#connections-and-config), and `--app-id`, the numeric **App ID** from the **App Info** panel. + ## Read the app's state The header shows the app's status and the one or two actions that make sense for it: **Deploy App** for an app that has never run, **Start App** for a stopped one, **Open App** and **Redeploy App** for a running one, and **Modify with Kai** on apps Kai built. The **⋯** menu holds the rest: **Suspend app**, **Copy app**, **Automate** (add it to a flow), **Debug mode**, **Delete app**. -The tabs below the header split the app's life into views: **Overview** (description, authentication, code source, App URL), **Advanced Settings** (environment variables and secrets, theme, data mappings), **All Runs** (every start attempt), **Terminal Logs** (stdout and stderr while it runs), **Versions** (every configuration change), and **Drafts** while Kai has a draft open. The **App Info** panel on the right shows the backend version and size, the auto-sleep timeout, the owner, and the App ID. +The tabs below the header split the app's life into views: **Overview** (the app's settings and its App URL), **Advanced Settings** (environment variables and secrets, theme, data mappings), **All Runs** (every start attempt), **Terminal Logs** (stdout and stderr while it runs), **Versions** (the configuration history), and **Drafts** while Kai has a draft open. The **App Info** panel on the right shows the backend version and size, the auto-sleep timeout, and the App ID. ## Ship a code change How depends on where the code lives; see [Two ways to run an app](/data-apps/what-are-apps/#two-ways-to-run-an-app). - **Kai built the app.** Click **Modify with Kai** and describe the change. Kai works in a draft with its own live preview; when you're happy, publish it to production. The production app keeps serving the old version until you do. Details: [Build your first app with Kai](/data-apps/getting-started/#how-drafts-become-production). -- **Your own repository.** Push to the branch the app follows, then click **Redeploy App**. Keboola clones the branch again, runs `setup.sh`, and starts the new version; the old one serves until the new one is up. Push alone changes nothing. +- **Your own repository.** Push to the branch set under **Git Repository** on the app's page, then click **Redeploy App**. Keboola clones that branch again, runs your repository's `keboola-config/setup.sh` if it has one, and starts the new version. Push alone changes nothing. The app can report **stopped** for a moment while it restarts, so give it a reload before you worry. + +From a terminal, a redeploy is `kbagent data-app deploy --project --app-id --wait`. ## Change settings -Authentication, secrets, environment variables, the Git branch, backend size, and the auto-sleep timeout are all edited on the app's page, and none of them takes effect until the app starts again. After saving, click **Redeploy App** (running app) or **Start App** (stopped app). The deploy wizard asks for the backend size and the inactivity timeout every time; keep the current values unless you mean to change them. +Authentication, secrets, environment variables and the Git branch are edited on the app's page; the backend size and the inactivity timeout are chosen in the deploy wizard each time you deploy or start the app. Nothing you edit on the page takes effect until the app runs again, so after saving click **Redeploy App** on a running app or **Start App** on a stopped one. The wizard asks for backend version, backend size and inactivity timeout every time; keep the current values unless you mean to change them. + +Secrets deserve one note: after you save one, the field is masked, and revealing it shows an encrypted value starting with `KBC::ProjectSecure`. That's the secret stored [encrypted](/extend/encryption/), not a replacement, and the rest of the prefix depends on which cloud your stack runs on. To rotate it, paste the new value over the encrypted one and redeploy. How secret names become environment variables: [Secrets](/data-apps/reference/#secrets). -Secrets deserve one note: after you save one, the field shows an encrypted value starting with `KBC::ProjectSecureGKMS::`. That's the secret stored encrypted, not a replacement. To rotate it, paste the new value over the encrypted one and redeploy. How secret names become environment variables: [Secrets](/data-apps/reference/#secrets). +From a terminal: `kbagent data-app secrets-set --project --app-id --secret '#API_KEY=…'`, then `kbagent data-app deploy`. ## Stop, start, sleep - **Sleeping** is automatic. After the inactivity timeout (five minutes to 24 hours, set in the deploy wizard) the app suspends; the next visit wakes it and shows a short **waking up** page. You pay only for time the app is awake or waiting to suspend. -- **Stopping** is deliberate: **⋯ → Suspend app** stops the container and takes the URL offline while keeping the configuration. Keboola asks you to confirm. **Start App** brings it back with the same settings. +- **Stopping** is deliberate: **⋯ → Suspend app** stops the container, so the app no longer serves, while keeping its configuration and its URL. **Start App** brings it back with the same settings. Use this when an app should be off for days; sleeping already covers being idle overnight. - **Redeploy App** restarts a running app with the current configuration and, for apps on your own repository, the current branch head. +From a terminal: `kbagent data-app stop` and `kbagent data-app start`, both with `--project` and `--app-id`. + Sleep and wake behaviour in detail, including the wakeup error page: [Sleep and resume](/data-apps/reference/#sleep-and-resume). -## See what changed +## See what changed, and go back + +The **Versions** tab lists the app's configuration history: who changed what, and when. An app is a component configuration like any other, so you can compare versions and [roll back](/components/#rollback-version) to an earlier one. A rollback changes the configuration only, so redeploy afterwards to put the old settings back in service. -The **Versions** tab lists every configuration change with who made it and when, from "Configuration created" onwards; **Show All Versions** opens the full history. Code changes for a Kai-built app are visible in the Builder's chat and drafts; for your own repository, in your Git history. +Code changes are tracked separately from configuration: for a Kai-built app they're in the Builder's chat and drafts, and for your own repository they're in your Git history. ## Copy, automate, delete - **Copy app** duplicates the configuration into a new app, so you can try a change without touching the one people use. - **Automate** adds the app to a flow, for example to redeploy it after the data it reads has been refreshed. -- **Delete app** stops the app and deletes its configuration. The App URL stops working immediately; there is no undo. +- **Delete app** stops the app and deletes its configuration. The App URL stops working immediately; there is no undo. The terminal equivalent, `kbagent data-app delete`, says the same thing in its own words: cascade, irreversible. ## When something is off diff --git a/src/content/docs/data-apps/publish-and-share.md b/src/content/docs/data-apps/publish-and-share.md index 0dfb7b7f4..219f17784 100644 --- a/src/content/docs/data-apps/publish-and-share.md +++ b/src/content/docs/data-apps/publish-and-share.md @@ -25,10 +25,10 @@ Share the app URL — found on the app's **Overview** tab (the **App URL** block ## Manage a deployed app -From the app's header you can **Modify with Kai**, **Open App**, and **Start App** / **Redeploy App** depending on its state. The **⋯** menu holds the rest — **Copy app**, **Automate** (add it to a flow), **Debug mode**, and **Delete app**. See [App actions](/data-apps/reference/#app-actions) in the reference. +From the app's header you can **Modify with Kai**, **Open App**, and **Start App** / **Redeploy App** depending on its state. The **⋯** menu holds the rest: **Suspend app**, **Copy app**, **Automate** (add it to a flow), **Debug mode**, and **Delete app**. [Operate and update an app](/data-apps/operate/) walks through each one; [App actions](/data-apps/reference/#app-actions) in the reference lists them. ![The app header with Modify with Kai / Open App / Start App and the ⋯ menu open showing Copy app, Automate, Debug mode, and Delete app](/data-apps/app-actions-menu.png) --- -**Next:** [Apps reference →](/data-apps/reference/) +**Next:** [Operate and update an app →](/data-apps/operate/) diff --git a/src/content/docs/data-apps/troubleshooting.md b/src/content/docs/data-apps/troubleshooting.md index d835ded77..1fcfd8982 100644 --- a/src/content/docs/data-apps/troubleshooting.md +++ b/src/content/docs/data-apps/troubleshooting.md @@ -17,16 +17,16 @@ Most app problems fall into a handful of patterns. Find where the evidence is fi kbagent data-app logs --project --app-id ``` - The first lists recent start attempts with their outcome; the second tails the container log. + The first lists recent start attempts with their outcome; the second tails the container log. `` is the name you gave the project when you [connected it](/cli/concepts/#connections-and-config); `` is the numeric **App ID** from the **App Info** panel on the app's page. - **Kai**, for an app it built: ask it why the app isn't running. It reads the same logs. ## The app doesn't start | What you see | Why | Fix | |---|---|---| -| **Data App is not running** right after a deploy, and the run lasted about a second | The repository lacks the `keboola-config/` folder, or part of it. The runtime log says `App must have keboola-config/nginx/ directory` (or `supervisord/`) and the container exits. This is the single most common start failure. | Add the folder; [what the repository must contain](/data-apps/build-locally/) lists exactly what goes in it. Check with `kbagent data-app validate-repo` before the next deploy. | +| **Data App is not running** right after a deploy, the run lasted about a second, and the log says `App must have keboola-config/nginx/ directory` (or `supervisord/`) | The repository lacks the `keboola-config/` folder, or part of it, so the container exits at once. This is the most common start failure we measure on customer stacks. | Add the folder; [what the repository must contain](/data-apps/build-locally/) lists what goes in it, and the app-building skill's [reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md) explains every line. Then check the repository before deploying again: `kbagent data-app validate-repo --git-repo https://github.com// --git-branch main --type python-js`. | | `fatal: Authentication failed` in the job log | Keboola couldn't clone the repository: the access token or SSH key for a private repository is missing, wrong, or expired. | Open **Git Repository** on the app, update **Username** and **Access Token** or the **SSH Private Key**, and redeploy. For a Keboola-managed repository this shouldn't happen; redeploy, and if it repeats, report it with the job ID. | -| The app starts and stops in a loop | The supervisord program can't start the process: a relative path where a full `/app/...` path is needed, a Python command without `uv run`, a `setup.sh` committed without the executable bit, or a `[program:nginx]` entry the image already provides. | Fix the config; the [layout contract](/data-apps/build-locally/) spells out each rule. | +| The app starts and stops in a loop | The supervisord program can't start the process: a relative path where a full `/app/...` path is needed, a Python command without `uv run`, a `setup.sh` committed without the executable bit, or a `[program:nginx]` entry the image already provides. | Fix the config; the [layout contract](/data-apps/build-locally/) spells out each rule, with the reasoning in the skill's [reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md). | | The install step fails | No `pyproject.toml` or `package.json` where `setup.sh` runs, or `pip install` in `setup.sh`; the image blocks bare `pip` (`externally-managed-environment`). | Commit the manifest; use `uv sync` or `npm install --omit=dev`. | | The deploy job fails on a package conflict | Two dependencies want incompatible versions, or an unpinned package moved. | Pin versions. Streamlit apps: [Lock package versions](/data-apps/streamlit/lock-version/). | @@ -37,15 +37,17 @@ Most app problems fall into a handful of patterns. Find where the evidence is fi | `Cannot POST /`, "Method Not Allowed", or a blank page on first open | Keboola checks the app with a POST to `/`, and the root route only accepts GET. | Accept POST on `/`: Flask `methods=["GET", "POST"]`, Express `app.all('/')`. Streamlit handles it on its own. | | Works locally, not in Keboola | The port in nginx's `proxy_pass` doesn't match the port the app listens on, or the app binds an interface nginx can't reach. | Make the ports match and listen on `127.0.0.1` or all interfaces. | | A streaming response arrives all at once | nginx buffers the whole response before forwarding it. | `proxy_buffering off; proxy_cache off; proxy_request_buffering off;` in that `location` block. | -| The page loads but never updates | The WebSocket handshake isn't upgraded. | Add the WebSocket upgrade directives to that `location` block; the app-building skill's nginx snippet has them. | +| The page loads but never updates | The WebSocket handshake isn't upgraded. | Add the WebSocket upgrade directives to that `location` block; the skill's [nginx reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md) has the snippet. | | An environment variable is undefined | The secret isn't in the app's configuration, or you're using the wrong name. | Add it; `#my-key` becomes `MY_KEY`. See [Secrets](/data-apps/reference/#secrets). | | A bundled frontend serves stale or missing files | The build output isn't committed. | Build locally and commit the output; Keboola installs dependencies, it doesn't run your build. | +| The app loads, but every query comes back empty, and the logs look clean | Storage Access is off, so Keboola never injects `WORKSPACE_ID`, `QUERY_SERVICE_URL` and `KBC_WORKSPACE_MANIFEST_PATH`. The app deploys and reports running while serving no data. | Turn on Storage Access for the app and redeploy. See [Data access](/data-apps/reference/#data-access). | | A Streamlit app shows an error in the browser, but the logs are empty | Streamlit renders uncaught exceptions in the page and doesn't write them to stderr. | Wrap your entry point so exceptions are logged to stderr before Streamlit shows them. | ## Sign-in problems - **Basic (Password):** the password is on the app's configuration page next to **Open App** once the app is deployed. -- **OIDC:** each provider tab on [Authentication](/data-apps/authentication/) ends with an "If sign-in fails" list for that provider's error texts, redirect-URI mismatches, and audience settings. +- **OIDC:** the Google, Entra, Okta and Auth0 tabs on [Authentication](/data-apps/authentication/) each end with an "If sign-in fails" list covering that provider's error texts, redirect-URI mismatches, and audience settings. +- **GitHub, GitLab, JumpCloud:** each has its own required fields, listed in its section of [Authentication](/data-apps/authentication/). A sign-in that fails immediately is usually an organization or group restriction rather than a wrong credential. - **Nobody can get in after you changed the authentication settings:** the change takes effect on the next start. Click **Redeploy App** (running app) or **Start App** (stopped app). ## Sleeping and waking diff --git a/src/content/docs/data-apps/what-are-apps.md b/src/content/docs/data-apps/what-are-apps.md index cb78bd22b..7df97f286 100644 --- a/src/content/docs/data-apps/what-are-apps.md +++ b/src/content/docs/data-apps/what-are-apps.md @@ -40,21 +40,20 @@ Have an existing **Streamlit** app? Streamlit remains supported; its documentati ## Two ways to run an app -Every app runs from a Git repository. Where that repository lives decides how you work on the app, so pick the path when you create it. +If the Kai builder and the live preview are missing from your app, its code lives in a repository you host. Those two features need a repository Keboola manages for the app. Everything else works the same either way, and here is what each path gives you. (A Streamlit app is the exception to all of this: it can run from inline code with no repository at all. See [Streamlit apps](/data-apps/streamlit/).) -**Kai and a Keboola-managed repository.** When Kai creates an app, Keboola also creates a private Git repository for it on your stack (`git..keboola.com/keboola/app-.git`) and Kai commits to it. That's what makes the rest of the Kai experience possible: the live preview, drafts you refine before publishing, and **Modify with Kai** later on. You never touch Git; you describe changes and approve them. This is the recommended path for most people, and the one [Build your first app with Kai](/data-apps/getting-started/) walks through. +**A Keboola-managed repository.** Keboola creates a private Git repository for the app on your stack, at `git..keboola.com/keboola/app-.git`. Kai commits to it, which is what makes the live preview, the drafts you refine before publishing, and **Modify with Kai** possible. You never touch Git yourself. This is the recommended path for most people, and the one [Build your first app with Kai](/data-apps/getting-started/) walks through. Kai isn't the only way in: `kbagent data-app create --use-managed-git-repo` provisions the same repository empty, and you push to it with credentials you mint yourself. -**Your own repository.** Point the app at a repository you host, on GitHub, GitLab, or any Git server, and Keboola clones it on every deploy. You develop wherever you like, with your own editor, your own review process, and coding assistants such as Claude Code using the [app-building skill](/data-apps/build-locally/#develop-with-an-ai-coding-tool). Kai can't edit code it doesn't host, so the Kai builder and its live preview aren't part of this path; you push a change and click **Redeploy**. Your repository has to follow a small [layout contract](/data-apps/build-locally/) so Keboola knows how to start it. [Build locally](/data-apps/build-locally/) covers it end to end, including the terminal path with `kbagent`. +**Your own repository.** Point the app at a repository you host, on GitHub, GitLab, or any Git server, and Keboola clones it on every deploy. You develop wherever you like, with your own editor and review process, and with coding assistants such as Claude Code using the [app-building skill](/data-apps/build-locally/#develop-with-an-ai-coding-tool). The Kai builder and its live preview aren't part of this path; you push a change and click **Redeploy App**. Your repository has to follow a small layout contract so Keboola knows how to start it, which [Build locally](/data-apps/build-locally/) covers end to end, along with the terminal path using `kbagent`. -Everything after the code is the same on both paths: the same runtime, the same [authentication](/data-apps/authentication/) options, the same App URL, the same deploy, redeploy, sleep, and logs. +Which kind of repository an app uses is decided when the app is created. Everything after the code is the same on both paths: the same runtime, the same [authentication](/data-apps/authentication/) options, the same App URL, deploy, redeploy, sleep, and logs. -| | Kai and a managed repository | Your own repository | +| | A Keboola-managed repository | Your own repository | |---|---|---| -| Who writes the code | Kai, from your description | You, or your coding assistant | -| Where it lives | A private repo on your Keboola stack | GitHub, GitLab, or any Git server | -| Changing the app | Tell Kai, preview the draft, publish | Push, then **Redeploy** | -| Live preview and drafts | Yes | No | -| Layout rules for the repo | Handled by Kai | The `keboola-config/` contract | +| Who writes the code | Kai, or you pushing to the managed repo | You, or your coding assistant | +| Kai builder, live preview, drafts | Yes | No | +| Changing the app | Tell Kai, preview the draft, publish | Push, then **Redeploy App** | +| Layout rules for the repo | Scaffolded for you when Kai builds the app; yours to meet if you push to it yourself | The `keboola-config/` contract is yours to meet | | Best for | Dashboards, internal tools, getting something in front of people fast | Existing code, teams with a review process, custom stacks | --- From b329e3c929487dc1093d09469be8a34ec312c3cf Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 23 Sep 2026 10:29:50 +0200 Subject: [PATCH 10/15] Second fact-check pass: fix what the first round's fixes got wrong MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The re-check caught three things I introduced yesterday and today: - "Every task here can also be done from a terminal with kbagent" is false, and the same commit added the counterexample. There is no rollback, copy, debug-mode or draft command in the CLI reference. Say which tasks have a terminal equivalent and which don't. - App Info does have an owner field. The committed screenshot shows Backend Version, Backend Size, Auto Sleep, Last Change, Owner, App ID. Restored, with Last Change. - Suspend app isn't in the ⋯ menu screenshot, which shows a stopped app. The menu is state-dependent, so say that rather than propagating a five-item list onto a page whose picture shows four. And three that were already wrong on main: - The deploy wizard screenshot has shown three fields all along. Four places described two. getting-started's alt text, publish-and-share and reference now match the picture, so nothing needs reshooting here. - reference.md's App actions listed five of the eight actions, in casing the UI doesn't use, and claimed a suspended app's URL is no longer available while the CLI says stop preserves it. Rewritten to the current labels, with the contested URL claim dropped from both pages rather than asserted on one. - The Storage Access row needed the project-level feature toggle, which comes before the per-app setting. Also dropped an unsupported generalization about GitHub, GitLab and JumpCloud sign-in failures, replaced with the restriction fields each provider actually has. JumpCloud has only allowed roles, so the original sentence didn't even fit the provider it was attached to. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/getting-started.md | 2 +- src/content/docs/data-apps/operate.md | 8 ++++---- src/content/docs/data-apps/publish-and-share.md | 4 ++-- src/content/docs/data-apps/reference.md | 15 +++++++++++---- src/content/docs/data-apps/troubleshooting.md | 4 ++-- src/content/docs/data-apps/what-are-apps.md | 2 +- 6 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/content/docs/data-apps/getting-started.md b/src/content/docs/data-apps/getting-started.md index efbef29d0..6726e9638 100644 --- a/src/content/docs/data-apps/getting-started.md +++ b/src/content/docs/data-apps/getting-started.md @@ -103,7 +103,7 @@ Prefer to set the app up yourself, without the chat? Manual creation lives on th 6. Click **Deploy App**. A short wizard asks for the **backend version**, the **backend size** and an **inactivity timeout**, then deploys the app. (The same wizard runs on **Redeploy**.) -![The deploy/redeploy wizard: backend size and inactivity timeout](/data-apps/deploy-timeout-backedsize.png) +![The deploy/redeploy wizard: backend version, backend size and inactivity timeout](/data-apps/deploy-timeout-backedsize.png) 7. When the status turns **Active**, click **Open App** to open it at its public URL. Use **Redeploy** to apply any later config change; see [App actions](/data-apps/reference/#app-actions). diff --git a/src/content/docs/data-apps/operate.md b/src/content/docs/data-apps/operate.md index a8f1c2017..2768891c1 100644 --- a/src/content/docs/data-apps/operate.md +++ b/src/content/docs/data-apps/operate.md @@ -6,13 +6,13 @@ description: "Day-to-day work on a deployed Keboola app: read its state, ship a Once an app is deployed, everything you do with it starts on its page under **Apps**. This page walks through the routine tasks; [Apps reference](/data-apps/reference/) has the full list of settings and actions. -Every task here can also be done from a terminal with `kbagent`. Those commands all take `--project `, the name you gave the project when you [connected it](/cli/concepts/#connections-and-config), and `--app-id`, the numeric **App ID** from the **App Info** panel. +Deploying, starting, stopping, deleting and setting secrets also work from a terminal with `kbagent`; drafts, copying and rollback are UI-only. The `kbagent` commands below all take `--project `, the name you gave the project when you [connected it](/cli/concepts/#connections-and-config), and `--app-id`, the numeric **App ID** from the **App Info** panel. ## Read the app's state -The header shows the app's status and the one or two actions that make sense for it: **Deploy App** for an app that has never run, **Start App** for a stopped one, **Open App** and **Redeploy App** for a running one, and **Modify with Kai** on apps Kai built. The **⋯** menu holds the rest: **Suspend app**, **Copy app**, **Automate** (add it to a flow), **Debug mode**, **Delete app**. +The header shows the app's status and the one or two actions that make sense for it: **Deploy App** for an app that has never run, **Start App** for a stopped one, **Open App** and **Redeploy App** for a running one, and **Modify with Kai** on apps Kai built. The **⋯** menu holds the rest, and its contents depend on the state too: **Copy app**, **Automate** (add it to a flow), **Debug mode** and **Delete app** are always there, with **Suspend app** on a running app. -The tabs below the header split the app's life into views: **Overview** (the app's settings and its App URL), **Advanced Settings** (environment variables and secrets, theme, data mappings), **All Runs** (every start attempt), **Terminal Logs** (stdout and stderr while it runs), **Versions** (the configuration history), and **Drafts** while Kai has a draft open. The **App Info** panel on the right shows the backend version and size, the auto-sleep timeout, and the App ID. +The tabs below the header split the app's life into views: **Overview** (the app's settings and its App URL), **Advanced Settings** (environment variables and secrets, theme, data mappings), **All Runs** (every start attempt), **Terminal Logs** (stdout and stderr while it runs), **Versions** (the configuration history), and **Drafts** while Kai has a draft open. The **App Info** panel on the right shows the backend version and size, the auto-sleep timeout, the last change, the owner, and the App ID. ## Ship a code change @@ -34,7 +34,7 @@ From a terminal: `kbagent data-app secrets-set --project --app-id - ## Stop, start, sleep - **Sleeping** is automatic. After the inactivity timeout (five minutes to 24 hours, set in the deploy wizard) the app suspends; the next visit wakes it and shows a short **waking up** page. You pay only for time the app is awake or waiting to suspend. -- **Stopping** is deliberate: **⋯ → Suspend app** stops the container, so the app no longer serves, while keeping its configuration and its URL. **Start App** brings it back with the same settings. Use this when an app should be off for days; sleeping already covers being idle overnight. +- **Stopping** is deliberate: **⋯ → Suspend app** stops the container and keeps the configuration, so the app stops serving until you bring it back with **Start App**. Use this when an app should be off for days; sleeping already covers being idle overnight. - **Redeploy App** restarts a running app with the current configuration and, for apps on your own repository, the current branch head. From a terminal: `kbagent data-app stop` and `kbagent data-app start`, both with `--project` and `--app-id`. diff --git a/src/content/docs/data-apps/publish-and-share.md b/src/content/docs/data-apps/publish-and-share.md index 219f17784..b3a855e91 100644 --- a/src/content/docs/data-apps/publish-and-share.md +++ b/src/content/docs/data-apps/publish-and-share.md @@ -10,7 +10,7 @@ Once your app is ready, publish it and share the link with the people who need i An app is "published" once it's deployed and reachable at its URL, with the right people able to open it: -1. **Deploy the app.** From the app's configuration, click **Deploy App**. A short wizard asks for the **backend size** and an **inactivity timeout**, then deploys it; when the status turns **Active**, it's served at its public URL. See [App actions](/data-apps/reference/#app-actions). +1. **Deploy the app.** From the app's configuration, click **Deploy App**. A short wizard asks for the **backend version**, the **backend size** and an **inactivity timeout**, then deploys it; when the status turns **Active**, it's served at its public URL. See [App actions](/data-apps/reference/#app-actions). ![The Deploy App wizard: Backend Version (Python/JS), Backend Size, and Inactivity Timeout, with a Deploy App button](/data-apps/deploy-timeout-backedsize.png) @@ -25,7 +25,7 @@ Share the app URL — found on the app's **Overview** tab (the **App URL** block ## Manage a deployed app -From the app's header you can **Modify with Kai**, **Open App**, and **Start App** / **Redeploy App** depending on its state. The **⋯** menu holds the rest: **Suspend app**, **Copy app**, **Automate** (add it to a flow), **Debug mode**, and **Delete app**. [Operate and update an app](/data-apps/operate/) walks through each one; [App actions](/data-apps/reference/#app-actions) in the reference lists them. +From the app's header you can **Modify with Kai**, **Open App**, and **Start App** / **Redeploy App** depending on its state. The **⋯** menu holds the rest: **Copy app**, **Automate** (add it to a flow), **Debug mode** and **Delete app**, plus **Suspend app** while the app is running. [Operate and update an app](/data-apps/operate/) walks through each one. ![The app header with Modify with Kai / Open App / Start App and the ⋯ menu open showing Copy app, Automate, Debug mode, and Delete app](/data-apps/app-actions-menu.png) diff --git a/src/content/docs/data-apps/reference.md b/src/content/docs/data-apps/reference.md index 148420acc..418b89195 100644 --- a/src/content/docs/data-apps/reference.md +++ b/src/content/docs/data-apps/reference.md @@ -181,9 +181,16 @@ Manage a deployed app from its actions menu. - **Deploy App** — starts the app. Once the deployment job finishes, open the app's public URL with **Open App**. - **Open App** — opens a new window with your app. -- **Redeploy** — apply changes made in the app configuration (they take effect only after a redeploy). -- **Suspend App** — stops the app. The container stops and the URL is no longer available, but the configuration is kept. -- **Delete App** — stops the deployment and deletes its configuration. +- **Redeploy App** — apply changes made in the app configuration (they take effect only after a redeploy). +- **Start App** — brings a stopped app back with the same settings. +- **Modify with Kai** — opens the Builder, on apps Kai built. +- **Suspend app** — stops the app. The container stops and the app no longer serves, but the configuration is kept. +- **Copy app** — duplicates the configuration into a new app. +- **Automate** — adds the app to a flow. +- **Debug mode** — runs the app with extra diagnostics. +- **Delete app** — stops the deployment and deletes its configuration. + +Which of these the header and the **⋯** menu offer depends on the app's state. [Operate and update an app](/data-apps/operate/) walks through them in the order you meet them. ## Sleep and resume @@ -201,7 +208,7 @@ If something goes wrong, a **wakeup error** page appears; click **Show More** fo ![Wakeup error](/data-apps/proxy-error-wakeing-up.png) -When you **Deploy** or **Redeploy**, a wizard prompts for the backend size and the auto-sleep timeout (five minutes to 24 hours; default five minutes). +When you **Deploy** or **Redeploy**, a wizard prompts for the backend version, the backend size and the auto-sleep timeout (five minutes to 24 hours; default five minutes). ![Deploy timeout and backend size](/data-apps/deploy-timeout-backedsize.png) diff --git a/src/content/docs/data-apps/troubleshooting.md b/src/content/docs/data-apps/troubleshooting.md index 1fcfd8982..cf0a2912a 100644 --- a/src/content/docs/data-apps/troubleshooting.md +++ b/src/content/docs/data-apps/troubleshooting.md @@ -40,14 +40,14 @@ Most app problems fall into a handful of patterns. Find where the evidence is fi | The page loads but never updates | The WebSocket handshake isn't upgraded. | Add the WebSocket upgrade directives to that `location` block; the skill's [nginx reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md) has the snippet. | | An environment variable is undefined | The secret isn't in the app's configuration, or you're using the wrong name. | Add it; `#my-key` becomes `MY_KEY`. See [Secrets](/data-apps/reference/#secrets). | | A bundled frontend serves stale or missing files | The build output isn't committed. | Build locally and commit the output; Keboola installs dependencies, it doesn't run your build. | -| The app loads, but every query comes back empty, and the logs look clean | Storage Access is off, so Keboola never injects `WORKSPACE_ID`, `QUERY_SERVICE_URL` and `KBC_WORKSPACE_MANIFEST_PATH`. The app deploys and reports running while serving no data. | Turn on Storage Access for the app and redeploy. See [Data access](/data-apps/reference/#data-access). | +| The app loads, but every query comes back empty, and the logs look clean | Storage Access is off, so Keboola never injects `WORKSPACE_ID`, `QUERY_SERVICE_URL` and `KBC_WORKSPACE_MANIFEST_PATH`. The app deploys and reports running while serving no data. | Turn on **Storage Access** for the app and redeploy. It also has to be enabled for the project first, under **Project Settings > Features**. See [Data access](/data-apps/reference/#data-access). | | A Streamlit app shows an error in the browser, but the logs are empty | Streamlit renders uncaught exceptions in the page and doesn't write them to stderr. | Wrap your entry point so exceptions are logged to stderr before Streamlit shows them. | ## Sign-in problems - **Basic (Password):** the password is on the app's configuration page next to **Open App** once the app is deployed. - **OIDC:** the Google, Entra, Okta and Auth0 tabs on [Authentication](/data-apps/authentication/) each end with an "If sign-in fails" list covering that provider's error texts, redirect-URI mismatches, and audience settings. -- **GitHub, GitLab, JumpCloud:** each has its own required fields, listed in its section of [Authentication](/data-apps/authentication/). A sign-in that fails immediately is usually an organization or group restriction rather than a wrong credential. +- **GitHub, GitLab, JumpCloud:** each has its own required fields and its own optional restrictions, which are the first thing to check when the right person is turned away. GitHub filters by organization, team, repository and allowed users; GitLab by group, project and allowed roles; JumpCloud by allowed roles. All three are in their sections of [Authentication](/data-apps/authentication/). - **Nobody can get in after you changed the authentication settings:** the change takes effect on the next start. Click **Redeploy App** (running app) or **Start App** (stopped app). ## Sleeping and waking diff --git a/src/content/docs/data-apps/what-are-apps.md b/src/content/docs/data-apps/what-are-apps.md index 7df97f286..8f415c97e 100644 --- a/src/content/docs/data-apps/what-are-apps.md +++ b/src/content/docs/data-apps/what-are-apps.md @@ -42,7 +42,7 @@ Have an existing **Streamlit** app? Streamlit remains supported; its documentati If the Kai builder and the live preview are missing from your app, its code lives in a repository you host. Those two features need a repository Keboola manages for the app. Everything else works the same either way, and here is what each path gives you. (A Streamlit app is the exception to all of this: it can run from inline code with no repository at all. See [Streamlit apps](/data-apps/streamlit/).) -**A Keboola-managed repository.** Keboola creates a private Git repository for the app on your stack, at `git..keboola.com/keboola/app-.git`. Kai commits to it, which is what makes the live preview, the drafts you refine before publishing, and **Modify with Kai** possible. You never touch Git yourself. This is the recommended path for most people, and the one [Build your first app with Kai](/data-apps/getting-started/) walks through. Kai isn't the only way in: `kbagent data-app create --use-managed-git-repo` provisions the same repository empty, and you push to it with credentials you mint yourself. +**A Keboola-managed repository.** Keboola creates a private Git repository for the app on your stack, at `git..keboola.com/keboola/app-.git`. Kai commits to it, which is what makes the live preview, the drafts you refine before publishing, and **Modify with Kai** possible. You never touch Git yourself. This is the recommended path for most people, and the one [Build your first app with Kai](/data-apps/getting-started/) walks through. Kai isn't the only way in: `kbagent data-app create --use-managed-git-repo` provisions the same repository empty, mints a credential for it, and pushes your code, so you can hold a managed repository and still write the code yourself. **Your own repository.** Point the app at a repository you host, on GitHub, GitLab, or any Git server, and Keboola clones it on every deploy. You develop wherever you like, with your own editor and review process, and with coding assistants such as Claude Code using the [app-building skill](/data-apps/build-locally/#develop-with-an-ai-coding-tool). The Kai builder and its live preview aren't part of this path; you push a change and click **Redeploy App**. Your repository has to follow a small layout contract so Keboola knows how to start it, which [Build locally](/data-apps/build-locally/) covers end to end, along with the terminal path using `kbagent`. From 21ecda744c915521d910c804d256290fe370bc6f Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 23 Sep 2026 12:05:45 +0200 Subject: [PATCH 11/15] Check the Entra, Okta and Auth0 tabs against the vendors' current docs These three were written from vendor documentation in September and have never been run against a live tenant. A documentation pass found one critical gap and several things that would cost a reader time. Okta, the critical one: on an Integrator Free Plan org the `default` authorization server ships without an access policy, so every token request fails until you add one, and nothing in our troubleshooting list matched that symptom. Also, the menu parenthetical was backwards (Applications and Resources is the Identity Engine label, Applications > Applications is Classic), the wizard may ask which experience to use, and `/login/signout` is not an endpoint Okta documents anywhere. The logout guidance now uses the end_session_endpoint from discovery, and step 4 no longer tells readers to delete the Sign-out redirect URI that the logout flow needs. The org authorization server is presented as Okta's own recommendation for plain SSO rather than as a fallback, because custom authorization servers need a paid add-on. Entra: the assignment flow stopped one click short of Assign, so nothing was saved. And testing the restriction as a Global Administrator gives a false pass, because the requirement does not apply to them, which is exactly the account a reader is likely signed in as. The role sentence named a floor where Microsoft documents alternatives. The claim most suspected of being stale, P1 or P2 for group assignment, turned out to be current and is unchanged. Auth0: a post-logout redirect has to be registered under Allowed Logout URLs or Auth0 refuses it, which we never mentioned. And the instruction to copy the issuer from discovery broke for custom-domain tenants, who have two domains and would have copied the wrong one. Discovery is now the primary instruction rather than the hedge; the trailing slash was confirmed against two real discovery documents. Still unverified and waiting on live runs: Okta's two error strings, which Okta does not publish, and whether either redirect-URI field arrives prefilled. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/authentication.mdx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/content/docs/data-apps/authentication.mdx b/src/content/docs/data-apps/authentication.mdx index cc8df744a..ccf945821 100644 --- a/src/content/docs/data-apps/authentication.mdx +++ b/src/content/docs/data-apps/authentication.mdx @@ -105,32 +105,32 @@ Pick your provider: Let people sign in with their Microsoft work account. - **Before you start** — you need permission to register applications in your tenant; the **Application Developer** role is enough for the registration itself. Restricting who can sign in needs **Cloud Application Administrator** or higher. + **Before you start** — you need permission to register applications in your tenant; the **Application Developer** role is enough for the registration itself. Restricting who can sign in needs **Cloud Application Administrator**, **Application Administrator**, or ownership of the app's service principal. **Register the app:** 1. Sign in to the [Microsoft Entra admin center](https://entra.microsoft.com/) and go to **Entra ID → App registrations → New registration**. 2. Enter a **Name** your users will recognize. - 3. Under **Supported account types**, keep **Single tenant only** (the option names your tenant): only users and guests of your tenant can sign in. + 3. Under **Supported account types**, keep the single-tenant option, which names your tenant: **Single tenant only**, or **Accounts in this organizational directory only** in older portal builds. Only users and guests of your tenant can sign in. 4. Under **Redirect URI (optional)**, choose **Web** and paste the callback URL from step 1. 5. Click **Register**. The app's **Overview** page opens. Copy the **Application (client) ID** and the **Directory (tenant) ID**; Keboola needs both. **Create a client secret:** 1. Under **Manage**, open **Certificates & secrets** and click **New client secret**. - 2. Enter a description, pick an expiry, and click **Add**. + 2. Enter a description, pick an expiry, and click **Add**. Microsoft caps the lifetime at 24 months and recommends under 12. 3. Copy the secret's **Value** right away; Entra hides it once you leave the page. Note the expiry, too: before it passes, create a new secret and update the app's authentication settings, or sign-in stops working. You now have: an Application (client) ID, a Directory (tenant) ID, and a client secret Value. - **Optional — restrict who can sign in.** Out of the box, anyone in your tenant can open the app. To let in only specific people or groups, go to **Entra ID → Enterprise apps**, open your app, and under **Manage → Properties** set **Assignment required?** to **Yes**. Then, under **Manage → Users and groups**, click **Add user/group** and pick who may sign in. Assigning groups, rather than single users, needs a Microsoft Entra ID P1 or P2 license. Everyone else now gets `AADSTS50105` at sign-in. + **Optional — restrict who can sign in.** Out of the box, anyone in your tenant can open the app. To let in only specific people or groups, go to **Entra ID → Enterprise apps**, open your app, and under **Manage → Properties** set **Assignment required?** to **Yes**. Then, under **Manage → Users and groups**, click **Add user/group**, pick who may sign in, leave **Select a role** on its default, and click **Assign**. Nothing is saved until you do. Assigning groups, rather than single users, needs a Microsoft Entra ID P1 or P2 license. Everyone else now gets `AADSTS50105` at sign-in, with one exception that will mislead you: the requirement doesn't apply to Global Administrators, so test with an ordinary account rather than your own. **Back in Keboola** — on the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Azure OIDC** in the **Provider** dropdown, and paste the **Client ID**, **Client secret**, and **Tenant ID**. Keboola builds the issuer from the tenant ID (`https://login.microsoftonline.com//v2.0`). Click **Save**. **If sign-in fails:** - `AADSTS50011: The redirect URI … does not match` — the redirect URI in the registration differs from the app's callback URL. Fix it under **Manage → Authentication**. - - `AADSTS7000215: Invalid client secret provided` — you pasted the secret's **Secret ID** instead of its **Value**, or the secret expired. Create a new one and update the app. + - `AADSTS7000215: Invalid client secret is provided` — the secret expired, or you pasted the secret's **Secret ID** instead of its **Value**. Create a new one and update the app. - `AADSTS50105: … is not assigned to a role for the application` — you required assignment and this user isn't assigned. Add them under **Enterprise apps → your app → Users and groups**. @@ -140,22 +140,22 @@ Pick your provider: **Create the app integration:** - 1. In the Okta Admin Console, go to **Applications → Applications** (in newer orgs the menu is called **Applications and Resources**) and click **Create App Integration**. + 1. In the Okta Admin Console, go to **Applications and Resources → Applications** (**Applications → Applications** in Classic Engine orgs) and click **Create App Integration**. If Okta asks which experience to use, choose **Classic experience**. 2. Choose **OIDC - OpenID Connect** as the **Sign-in method** and **Web Application** as the **Application type**, then click **Next**. 3. Enter an **App integration name**, for example `Keboola app - Toy store sales`. - 4. Under **Sign-in redirect URIs**, replace the default value with the callback URL from step 1. You can remove the default **Sign-out redirect URIs** entry. + 4. Under **Sign-in redirect URIs**, enter the callback URL from step 1. Leave an entry under **Sign-out redirect URIs** if you plan to set a **Logout URL** in Keboola later; Okta only returns people to a URI registered there. 5. Under **Assignments**, decide who can sign in: **Allow everyone in your organization to access** or **Limit access to selected groups** (or skip the assignment for now and add people later). 6. Click **Save**. On the **General** tab, under **Client Credentials**, copy the **Client ID** and the **Client secret**. You now have: a Client ID and a Client secret, and Okta knows your callback URL. - **Back in Keboola** — on the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Okta** in the **Provider** dropdown, and paste the **Client ID** and **Client secret**. Set **Domain/Org URL** to `https:///oauth2/default`; your Okta domain is shown in the Admin Console when you click your name at the top right, for example `https://acme.okta.com/oauth2/default`. The `default` authorization server comes with the Integrator Free Plan and with API Access Management; if **Security → API → Authorization Servers** doesn't list it, use the org authorization server instead: `https://` with no path. **Logout URL** is optional: `https:///login/signout` also ends the Okta session when someone signs out of the app. Click **Save**. + **Back in Keboola** — on the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Okta** in the **Provider** dropdown, and paste the **Client ID** and **Client secret**. Set **Domain/Org URL** to the issuer of the authorization server you want to use; your Okta domain is shown in the Admin Console when you click your name at the top right. For plain single sign-on, Okta points you at the **org authorization server**, whose issuer is the bare domain with no path, for example `https://acme.okta.com`. Custom authorization servers need API Access Management, a paid add-on in production orgs. If your org has one, its issuer is `https://acme.okta.com/oauth2/default`, which is also what the field's example shows, and you can check under **Security → API → Authorization Servers**. One trap on an Integrator Free Plan org: the `default` server there ships **without an access policy**, and every token request fails until you add one. Open **Security → API → default → Access Policies** and add a policy that applies to your app. **Logout URL** is optional: use the `end_session_endpoint` from the discovery document of whichever server you chose, at `https:///oauth2/default/.well-known/openid-configuration` or `https:///.well-known/openid-configuration` for the org server. Okta returns people to a URI registered under **Sign-out redirect URIs**, so keep one there that matches. Click **Save**. **If sign-in fails:** - "The 'redirect_uri' parameter must be a Login redirect URI in the client app settings" — the URI in the integration differs from the app's callback URL. Fix **Sign-in redirect URIs** on the integration's **General** tab. - "User is not assigned to the client application" — the user, or their group, isn't assigned to the integration. Add them on the **Assignments** tab. - - Issuer or discovery error when the app starts the sign-in — check **Domain/Org URL**: `https://`, your Okta domain, `/oauth2/default`, no trailing slash. If your org has no `default` authorization server, use `https://` instead. + - Issuer or discovery error when the app starts the sign-in — check **Domain/Org URL** against the `issuer` in that server's discovery document, character for character, with no trailing slash. If sign-in reaches Okta and then fails on a free-plan org, check that the `default` authorization server has an access policy. Let people sign in through Auth0, with whatever connections your tenant offers: username and password, social logins, or enterprise identity providers. @@ -167,16 +167,16 @@ Pick your provider: 1. In the [Auth0 Dashboard](https://manage.auth0.com/), go to **Applications → Applications** and click **Create Application**. 2. Name it, choose **Regular Web Applications**, and click **Create**. 3. Open the **Settings** tab. Under **Basic Information**, copy the **Domain**, **Client ID**, and **Client Secret**. - 4. Scroll down to **Application URIs** and paste the callback URL from step 1 into **Allowed Callback URLs**. + 4. Scroll down to **Application URIs** and paste the callback URL from step 1 into **Allowed Callback URLs**. If you plan to set a **Logout URL** in Keboola, add the app's URL to **Allowed Logout URLs** in the same block; Auth0 refuses any post-logout redirect that isn't registered there. 5. Click **Save Changes** at the bottom of the page. You now have: your tenant Domain, a Client ID, and a Client Secret. - **Back in Keboola** — on the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Auth0** in the **Provider** dropdown, and paste the **Client ID** and **Client secret**. Set **Issuer URL** to your tenant **Domain** with `https://` in front and a trailing slash, for example `https://acme.us.auth0.com/` (or a custom domain such as `https://login.acme.com/`). The field's example omits the slash, but Auth0's issuer ends with one; when in doubt, copy the `issuer` value from `https:///.well-known/openid-configuration`. **Logout URL** is optional: `https:///oidc/logout` also ends the Auth0 session when someone signs out of the app. Click **Save**. + **Back in Keboola** — on the app's configuration page, under **Authentication**, set **Authentication Type** to **OIDC (Custom)**, select **Auth0** in the **Provider** dropdown, and paste the **Client ID** and **Client secret**. Set **Issuer URL** to the `issuer` value from your tenant's discovery document, at `https:///.well-known/openid-configuration`. That's your custom domain if you have one, otherwise the **Domain** from step 3, and the two produce different issuers. Copy it character for character. Auth0 issuers normally end in a slash, for example `https://acme.us.auth0.com/` or `https://login.acme.com/`, while the field's own example omits it. **Logout URL** is optional: `https:///oidc/logout` also ends the Auth0 session when someone signs out of the app. Click **Save**. **If sign-in fails:** - - "Callback URL mismatch" on an Auth0 error page — the URL in **Allowed Callback URLs** differs from the app's callback URL. Fix it under **Settings → Application URIs**. + - "Callback URL Mismatch" on an Auth0 error page, with a line reading "{URL} is not in the list of allowed callback URLs" — the URL in **Allowed Callback URLs** differs from the app's callback URL. Fix it under **Settings → Application URIs**. - Issuer mismatch or discovery error when the app starts the sign-in — the **Issuer URL** must match the `issuer` in Auth0's discovery document character for character: `https://`, your Auth0 domain, trailing slash. - Users of one connection can't sign in — that connection isn't enabled for this application. Turn it on under the application's **Connections** tab. From c26a01ebb9e0b895a54d9dfc69dec996bcbab115 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 23 Sep 2026 15:03:54 +0200 Subject: [PATCH 12/15] UT-5646: point at the Callback URL field, and repair a cut-off sentence David's review makes the same point his Linear issue does: nobody should be reconstructing a URL path by hand. keboola/ui#9112 adds a read-only Callback URL with a copy button under Authentication, for OIDC, GitHub, GitLab and JumpCloud. Step 1 now sends readers there first, and the four provider sections that used to say "the whole host from the App URL block plus /_proxy/callback" now say to copy the field. Manual construction stays as the fallback, phrased as "no such field?", because the UI change has not shipped everywhere yet and the page has to work on both. It also now covers an app created without a URL prefix, whose callback URL has no hyphenated part at all. That case came out of UT-5646's acceptance criteria and was missing from every version of this page. Separately, the Callback URL format section ended mid-sentence, on an open parenthesis, where main has the complete text. Restored and extended with the no-prefix form. Closes the last unticked box on UT-5646. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/authentication.mdx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/content/docs/data-apps/authentication.mdx b/src/content/docs/data-apps/authentication.mdx index f768d9e2e..342258a78 100644 --- a/src/content/docs/data-apps/authentication.mdx +++ b/src/content/docs/data-apps/authentication.mdx @@ -52,7 +52,9 @@ The flow is the same everywhere: open the app in Keboola, register it with your Your provider needs the app's callback URL, so start in Keboola. 1. In your Keboola project, open **Apps** and click the app. It doesn't matter whether Kai built it or you created it yourself. No app yet? [Create one manually](/data-apps/getting-started/#create-an-app-manually) first; it opens on its configuration page. -2. On the app's **Overview** tab, scroll to the **App URL** block. It shows the app's host as a URL prefix plus a generated part, for example `toy-store-sales` and `-74016144.hub.europe-west3.gcp.keboola.com`. Click the block's copy button — it gives you the app's URL, **not** the callback URL. Paste it where you need it and add `/_proxy/callback` to the end: +2. Under **Authentication**, find the read-only **Callback URL** field and click its copy button. That is the exact value your provider needs. It appears for OIDC, GitHub, GitLab and JumpCloud, and not for None or Basic, which need no callback. + + No such field? Build the URL yourself from the **App URL** block on the **Overview** tab. That block shows the app's host as a URL prefix plus a generated part, for example `toy-store-sales` and `-74016144.hub.europe-west3.gcp.keboola.com`, and its copy button gives you the app's URL rather than the callback URL. Add `/_proxy/callback` to the end: ``` https://-.hub./_proxy/callback @@ -60,7 +62,9 @@ Your provider needs the app's callback URL, so start in Keboola. For example: `https://toy-store-sales-74016144.hub.europe-west3.gcp.keboola.com/_proxy/callback` - The block is there from the moment the app exists; you don't have to deploy first. + An app created without a URL prefix has no hyphenated part, so its callback URL is `https://.hub./_proxy/callback`. + + Either way, the value exists from the moment the app does; you don't have to deploy first. The block sits below **Authentication**: @@ -248,7 +252,7 @@ Restrict access to your app using GitHub OAuth. Users authenticate via their Git ### Setup instructions 1. Go to your GitHub account **Settings > Developer Settings > OAuth Apps** and create a new OAuth App. -2. Set the **Authorization callback URL** to: the app's callback URL — `https://-.hub./_proxy/callback`, the whole host from the **App URL** block plus `/_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). +2. Set the **Authorization callback URL** to the app's callback URL. Copy it from the **Callback URL** field under **Authentication**, or build it as `https://-.hub./_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). 3. Copy the **Client ID** and **Client Secret** from the created OAuth App. 4. In your Keboola app configuration, select **GitHub** as the authentication method. 5. Paste the **Client ID** and **Client Secret**. @@ -279,7 +283,7 @@ Restrict access to your app using GitLab OAuth. Users authenticate via their Git ### Setup instructions 1. Go to your GitLab instance **Settings > Applications** and create a new application. -2. Set the **Redirect URI** to: the app's callback URL — `https://-.hub./_proxy/callback`, the whole host from the **App URL** block plus `/_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). +2. Set the **Redirect URI** to the app's callback URL. Copy it from the **Callback URL** field under **Authentication**, or build it as `https://-.hub./_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). 3. Ensure the `openid`, `profile`, and `email` scopes are selected. If you use group or project restrictions, also select `read_api`. 4. Copy the **Application ID** and **Secret**. 5. In your Keboola app configuration, select **GitLab** as the authentication method. @@ -310,7 +314,7 @@ Restrict access to your app using JumpCloud OIDC. Users authenticate via their J 1. In the **JumpCloud Admin Console**, go to **SSO** and create a new application (or use an existing one). 2. Configure the application as an **OIDC** application. -3. Set the **Redirect URI** to: the app's callback URL — `https://-.hub./_proxy/callback`, the whole host from the **App URL** block plus `/_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). +3. Set the **Redirect URI** to the app's callback URL. Copy it from the **Callback URL** field under **Authentication**, or build it as `https://-.hub./_proxy/callback` (e.g., `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback`). 4. Copy the **Client ID** and **Client Secret** from the SSO tab. 5. In your Keboola app configuration, select **JumpCloud** as the authentication method. 6. Paste the **Client ID**, **Client Secret**, **Issuer URL**, and **Logout URL**. @@ -327,7 +331,9 @@ https://-.hub./_proxy/callback For example: `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback` -`-.hub.` is the whole host shown in the **App URL** block on the app's configuration page: the URL prefix, a hyphen, and the App ID ( +`-.hub.` is the whole host shown in the **App URL** block on the app's configuration page: the URL prefix, a hyphen, and the App ID (for example `toy-store-sales-74016144`). An app created without a URL prefix drops the hyphenated part and uses `https://.hub./_proxy/callback`. + +You don't have to assemble any of this by hand. The **Callback URL** field under **Authentication** shows the finished value with a copy button, for every method that needs one. --- From 134fb9737b9cf5309a109dfb4eb9f4ddee282aa2 Mon Sep 17 00:00:00 2001 From: Nikita Date: Wed, 23 Sep 2026 17:50:04 +0200 Subject: [PATCH 13/15] Lead the callback reference with the field too, not the format The step already pointed at the Callback URL field, but the reference section at the bottom still opened with the hand-assembled format and mentioned the field only in its last line. That is the same order David objected to: manual work first, the easy way as an afterthought. Turned round. The section now opens with "copy it from the field", says why copying beats typing (one wrong character reads as a redirect mismatch, not as a typo, which is exactly how the usability test ended), and keeps the format for reading a URL you already have or for a stack where the field has not appeared yet. Co-Authored-By: Claude Opus 5 --- src/content/docs/data-apps/authentication.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/content/docs/data-apps/authentication.mdx b/src/content/docs/data-apps/authentication.mdx index 342258a78..0805b39e0 100644 --- a/src/content/docs/data-apps/authentication.mdx +++ b/src/content/docs/data-apps/authentication.mdx @@ -323,7 +323,9 @@ Restrict access to your app using JumpCloud OIDC. Users authenticate via their J ## Callback URL format -All authentication methods that use OAuth or OIDC require a callback URL. The format is always: +Every method that uses OAuth or OIDC needs a callback URL, and the app's configuration gives you the finished value: the **Callback URL** field under **Authentication**, with a copy button. Copy it rather than typing it. A single wrong character is the most common reason sign-in fails, and the provider reports it as a redirect mismatch rather than as a typo. + +The rest of this section is for reading a URL you already have, or for a stack where the field hasn't appeared yet. ``` https://-.hub./_proxy/callback @@ -331,9 +333,7 @@ https://-.hub./_proxy/callback For example: `https://my-app-12345678.hub.north-europe.azure.keboola.com/_proxy/callback` -`-.hub.` is the whole host shown in the **App URL** block on the app's configuration page: the URL prefix, a hyphen, and the App ID (for example `toy-store-sales-74016144`). An app created without a URL prefix drops the hyphenated part and uses `https://.hub./_proxy/callback`. - -You don't have to assemble any of this by hand. The **Callback URL** field under **Authentication** shows the finished value with a copy button, for every method that needs one. +`-.hub.` is the whole host shown in the **App URL** block on the app's configuration page: the URL prefix, a hyphen, and the App ID, for example `toy-store-sales-74016144`. An app created without a URL prefix has no hyphenated part, so its callback URL is `https://.hub./_proxy/callback`. --- From 8ef0962e597925c6b299507b9b9b8d8f01743bb1 Mon Sep 17 00:00:00 2001 From: Nikita Zverev Date: Thu, 24 Sep 2026 12:11:51 +0000 Subject: [PATCH 14/15] PRDCT-692: point the layout-contract links at the ai-kit reference Three links promised a repository layout contract on Build locally. Jordan asked (#1128) to refer readers to the skill files instead, so the contract stays in references/python-js-apps.md in keboola/ai-kit and the links go straight there. This PR no longer depends on #1128. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r --- src/content/docs/data-apps/troubleshooting.md | 4 ++-- src/content/docs/data-apps/what-are-apps.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/content/docs/data-apps/troubleshooting.md b/src/content/docs/data-apps/troubleshooting.md index cf0a2912a..f644238dc 100644 --- a/src/content/docs/data-apps/troubleshooting.md +++ b/src/content/docs/data-apps/troubleshooting.md @@ -24,9 +24,9 @@ Most app problems fall into a handful of patterns. Find where the evidence is fi | What you see | Why | Fix | |---|---|---| -| **Data App is not running** right after a deploy, the run lasted about a second, and the log says `App must have keboola-config/nginx/ directory` (or `supervisord/`) | The repository lacks the `keboola-config/` folder, or part of it, so the container exits at once. This is the most common start failure we measure on customer stacks. | Add the folder; [what the repository must contain](/data-apps/build-locally/) lists what goes in it, and the app-building skill's [reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md) explains every line. Then check the repository before deploying again: `kbagent data-app validate-repo --git-repo https://github.com// --git-branch main --type python-js`. | +| **Data App is not running** right after a deploy, the run lasted about a second, and the log says `App must have keboola-config/nginx/ directory` (or `supervisord/`) | The repository lacks the `keboola-config/` folder, or part of it, so the container exits at once. This is the most common start failure we measure on customer stacks. | Add the folder; the app-building skill's [reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md) lists what goes in it and explains every line. Then check the repository before deploying again: `kbagent data-app validate-repo --git-repo https://github.com// --git-branch main --type python-js`. | | `fatal: Authentication failed` in the job log | Keboola couldn't clone the repository: the access token or SSH key for a private repository is missing, wrong, or expired. | Open **Git Repository** on the app, update **Username** and **Access Token** or the **SSH Private Key**, and redeploy. For a Keboola-managed repository this shouldn't happen; redeploy, and if it repeats, report it with the job ID. | -| The app starts and stops in a loop | The supervisord program can't start the process: a relative path where a full `/app/...` path is needed, a Python command without `uv run`, a `setup.sh` committed without the executable bit, or a `[program:nginx]` entry the image already provides. | Fix the config; the [layout contract](/data-apps/build-locally/) spells out each rule, with the reasoning in the skill's [reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md). | +| The app starts and stops in a loop | The supervisord program can't start the process: a relative path where a full `/app/...` path is needed, a Python command without `uv run`, a `setup.sh` committed without the executable bit, or a `[program:nginx]` entry the image already provides. | Fix the config; the skill's [reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md) spells out each rule and the reasoning behind it. | | The install step fails | No `pyproject.toml` or `package.json` where `setup.sh` runs, or `pip install` in `setup.sh`; the image blocks bare `pip` (`externally-managed-environment`). | Commit the manifest; use `uv sync` or `npm install --omit=dev`. | | The deploy job fails on a package conflict | Two dependencies want incompatible versions, or an unpinned package moved. | Pin versions. Streamlit apps: [Lock package versions](/data-apps/streamlit/lock-version/). | diff --git a/src/content/docs/data-apps/what-are-apps.md b/src/content/docs/data-apps/what-are-apps.md index 8f415c97e..fee8eef61 100644 --- a/src/content/docs/data-apps/what-are-apps.md +++ b/src/content/docs/data-apps/what-are-apps.md @@ -44,7 +44,7 @@ If the Kai builder and the live preview are missing from your app, its code live **A Keboola-managed repository.** Keboola creates a private Git repository for the app on your stack, at `git..keboola.com/keboola/app-.git`. Kai commits to it, which is what makes the live preview, the drafts you refine before publishing, and **Modify with Kai** possible. You never touch Git yourself. This is the recommended path for most people, and the one [Build your first app with Kai](/data-apps/getting-started/) walks through. Kai isn't the only way in: `kbagent data-app create --use-managed-git-repo` provisions the same repository empty, mints a credential for it, and pushes your code, so you can hold a managed repository and still write the code yourself. -**Your own repository.** Point the app at a repository you host, on GitHub, GitLab, or any Git server, and Keboola clones it on every deploy. You develop wherever you like, with your own editor and review process, and with coding assistants such as Claude Code using the [app-building skill](/data-apps/build-locally/#develop-with-an-ai-coding-tool). The Kai builder and its live preview aren't part of this path; you push a change and click **Redeploy App**. Your repository has to follow a small layout contract so Keboola knows how to start it, which [Build locally](/data-apps/build-locally/) covers end to end, along with the terminal path using `kbagent`. +**Your own repository.** Point the app at a repository you host, on GitHub, GitLab, or any Git server, and Keboola clones it on every deploy. You develop wherever you like, with your own editor and review process, and with coding assistants such as Claude Code using the [app-building skill](/data-apps/build-locally/#develop-with-an-ai-coding-tool). The Kai builder and its live preview aren't part of this path; you push a change and click **Redeploy App**. Your repository has to follow a small layout contract so Keboola knows how to start it; the app-building skill's [reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md) spells it out, and [Build locally](/data-apps/build-locally/) covers developing and syncing your own repository, along with the terminal path using `kbagent`. Which kind of repository an app uses is decided when the app is created. Everything after the code is the same on both paths: the same runtime, the same [authentication](/data-apps/authentication/) options, the same App URL, deploy, redeploy, sleep, and logs. From 659a68a5b7611e3c0ade8eff9cee204e9fc3d5d7 Mon Sep 17 00:00:00 2001 From: Nikita Zverev Date: Thu, 24 Sep 2026 12:16:12 +0000 Subject: [PATCH 15/15] PRDCT-692: Build locally has no kbagent; send the terminal path to Operate Build locally never mentions kbagent, on main or here. The kbagent data-app commands live on Operate and update an app, so the sentence points there. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01PEKnDNvPqkNrBonYN6S61r --- src/content/docs/data-apps/what-are-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/content/docs/data-apps/what-are-apps.md b/src/content/docs/data-apps/what-are-apps.md index fee8eef61..d229158a3 100644 --- a/src/content/docs/data-apps/what-are-apps.md +++ b/src/content/docs/data-apps/what-are-apps.md @@ -44,7 +44,7 @@ If the Kai builder and the live preview are missing from your app, its code live **A Keboola-managed repository.** Keboola creates a private Git repository for the app on your stack, at `git..keboola.com/keboola/app-.git`. Kai commits to it, which is what makes the live preview, the drafts you refine before publishing, and **Modify with Kai** possible. You never touch Git yourself. This is the recommended path for most people, and the one [Build your first app with Kai](/data-apps/getting-started/) walks through. Kai isn't the only way in: `kbagent data-app create --use-managed-git-repo` provisions the same repository empty, mints a credential for it, and pushes your code, so you can hold a managed repository and still write the code yourself. -**Your own repository.** Point the app at a repository you host, on GitHub, GitLab, or any Git server, and Keboola clones it on every deploy. You develop wherever you like, with your own editor and review process, and with coding assistants such as Claude Code using the [app-building skill](/data-apps/build-locally/#develop-with-an-ai-coding-tool). The Kai builder and its live preview aren't part of this path; you push a change and click **Redeploy App**. Your repository has to follow a small layout contract so Keboola knows how to start it; the app-building skill's [reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md) spells it out, and [Build locally](/data-apps/build-locally/) covers developing and syncing your own repository, along with the terminal path using `kbagent`. +**Your own repository.** Point the app at a repository you host, on GitHub, GitLab, or any Git server, and Keboola clones it on every deploy. You develop wherever you like, with your own editor and review process, and with coding assistants such as Claude Code using the [app-building skill](/data-apps/build-locally/#develop-with-an-ai-coding-tool). The Kai builder and its live preview aren't part of this path; you push a change and click **Redeploy App**. Your repository has to follow a small layout contract so Keboola knows how to start it; the app-building skill's [reference](https://github.com/keboola/ai-kit/blob/main/plugins/dataapp-developer/skills/dataapp-development/references/python-js-apps.md) spells it out, and [Build locally](/data-apps/build-locally/) covers developing and syncing your own repository; deploying and running it from a terminal with `kbagent data-app` is on [Operate and update an app](/data-apps/operate/). Which kind of repository an app uses is decided when the app is created. Everything after the code is the same on both paths: the same runtime, the same [authentication](/data-apps/authentication/) options, the same App URL, deploy, redeploy, sleep, and logs.