Skip to content

The dukkan.app.toml file

Every key of the app configuration file, how the CLI syncs it with the portal, and what happens when the two disagree.

Updated 9 Sept 20263 min read
On this page

This page is for anyone whose app carries a dukkan.app.toml. By the end you know what every key means, which values the portal validates, and how dukkan app config push and pull keep the file and your draft version in step.

What the file is#

dukkan.app.toml is the declarative description of your app: its name, the scopes it asks for, the exact redirect URIs, the webhook topics, and the paths the SDK mounts. Three things read it:

  • @dukkan.one/nuxt at build time, to mount the install, callback and webhook routes.
  • dukkan app config push, to turn it into the draft version of your app in the portal.
  • dukkan app dev, for the port and the tunnel provider.

Secrets never live in it. The client secret, the session secret and your database URL come from the environment; see The Nuxt module.

A complete file#

dukkan.app.tomlText
config_version = 1

[app]
name = "Order notifier"
client_id = "dukkan_app_1f3c9e2a"
api_version = "v1"

[auth]
scopes = ["orders:read", "orders:write"]
redirect_uris = [
  "https://app.example.com/dukkan/callback",
  "http://localhost:3000/dukkan/callback",
]

[webhooks]
topics = ["order.created", "order.paid", "app.uninstalled"]

[urls]
app_url = "https://app.example.com"
install_path = "/dukkan/install"
callback_path = "/dukkan/callback"
webhook_path = "/dukkan/webhooks"

[dev]
port = 3000
tunnel = "cloudflared"

Keys#

Key Required Meaning
config_version yes Always 1 today. A later revision of the file format bumps it.
app.name yes Display name in the portal, 1 to 120 characters.
app.client_id no The OAuth client id. dukkan app config pull fills it in; @dukkan.one/nuxt uses it as the default for NUXT_DUKKAN_CLIENT_ID.
app.handle no The marketplace handle once the app is listed: lowercase letters, digits and hyphens.
app.api_version no v1. The dated revision the SDK speaks is pinned by the package, not by this file.
auth.scopes yes At least one scope from the scope reference. The consent screen shows exactly these.
auth.redirect_uris yes 1 to 10 exact URLs. https anywhere, http only on localhost, 127.0.0.1 or [::1]; no credentials, no fragment.
webhooks.topics no Topics from the topic table. Every topic's required scope must be in auth.scopes; the file is rejected otherwise.
urls.app_url no The public origin of the deployed app; the default for NUXT_DUKKAN_APP_BASE_URL.
urls.install_path no Where the marketplace sends merchants. Default /dukkan/install.
urls.callback_path no The OAuth redirect path. Default /dukkan/callback. Its full URL must appear in auth.redirect_uris.
urls.webhook_path no Where the platform posts deliveries. Default /dukkan/webhooks.
dev.port no The port dukkan app dev starts the app on. Default 3000.
dev.tunnel no cloudflared (default) or none. With none, pass --tunnel-url or run with --no-tunnel.

Paths start with / and contain only letters, digits, /, _ and -.

Validation#

The same schema runs in three places: the CLI before a push, the portal when it receives one, and the Nuxt module at build time. A JSON Schema is published at /contracts/v1/dukkan.app.schema.json for editor completion and for other languages; see The contract for other languages.

A failing file prints every problem with its path:

Text
✗ dukkan.app.toml is invalid:
  auth.redirect_uris.0: Redirect URIs must be https, or http on localhost
  webhooks.topics: These topics need scopes the app does not request: order.paid

Push and pull#

dukkan app config push sends the file's surface (scopes, redirect URIs, topics) to your app's draft version and records the file as the source. The portal then shows the version as Managed by dukkan.app.toml with the time of the push. A push never activates a version; releasing stays in the portal.

Text
Updating draft version 4:
  + scope: orders:write
  + redirect: https://app.example.com/dukkan/callback
  - topic: product.updated
✓ Updated draft version 4; the portal now shows it as managed by dukkan.app.toml.

dukkan app config pull writes the portal's draft (or the active version when there is no draft) back into the file, including app.client_id.

Warning

Removing a scope narrows what future installs consent to; existing installs keep what they granted. The CLI asks before pushing a removal unless you pass --yes.

When the portal and the file disagree#

Editing the draft in the portal after a push marks the version as edited by hand. The portal shows a Portal and dukkan.app.toml disagree notice with the pull command, and the next push refuses:

Text
✗ The portal draft changed since your last pull (last push 2026-09-08T14:02:11Z, then edited in the portal). Run: dukkan app config pull, or push again with --force to overwrite it.

Pull to adopt the portal's edits into the file, or push with --force to overwrite them. A draft under marketplace review is frozen either way; withdraw the submission first.

Next steps#