Canvas CLI
Getting Started #
Installation using pip #
To install the Canvas CLI using pip, execute pip install canvas. Python 3.11, 3.12, or 3.13 is required.
To upgrade the Canvas CLI if you installed using pip, execute pip install --upgrade canvas.
Installation using uv #
To install the Canvas CLI using uv, execute uv tool install canvas. uv will find or procure an acceptable Python version.
To upgrade the Canvas CLI if you installed using uv, execute uv tool upgrade canvas.
Signing in to Canvas Platform #
The Canvas CLI signs in to Canvas Platform, which hosts each plugin’s git repository and deploys it to the instances your organizations manage. Sign in once per machine:
$ canvas login
Signed in to https://platform.canvasmedical.com as you@example.com.
Acme Health (acme): plugin prefix acme__
canvas login opens your browser and also prints the sign-in URL, for a machine without a browser. It lists each organization you belong to with its plugin prefix. The session is stored in ~/.canvas/platform-credentials.json, readable only by you, and refreshes itself. canvas logout revokes it. A CI/CD pipeline signs in with a service account instead.
A plugin deployed through Canvas Platform has a publisher-prefixed name, <org prefix>__<package> (for example acme__intake), used for the manifest name, the package folder and its imports. The prefix decides which organization publishes the plugin.
canvas login --platform <url> or the CANVAS_PLATFORM_URL environment variable points the CLI at a platform other than https://platform.canvasmedical.com, and later commands keep using the platform you last signed in to.
canvas whoami shows which platform the CLI is using, why, and who you are signed in as there:
$ canvas whoami
Platform: https://platform.canvasmedical.com (from your last `canvas login`)
Signed in as you@example.com (browser session from `canvas login`)
Acme Health (acme): plugin prefix acme__, can push, deploy, configure, uninstall
You are now ready to use the Canvas CLI. Continue with canvas init and canvas deploy.
Service accounts for CI/CD #
The Canvas CLI supports service accounts for automated pipelines such as GitHub Actions, GitLab CI or any other CI/CD system. A service account is an identity that belongs to your organization rather than to a person, so a pipeline that publishes and deploys plugins keeps working when the person who set it up leaves, and Canvas Platform’s audit log names the service account behind each push and deploy. A pipeline signs in with the account’s token instead of canvas login, which needs a browser.
Create a service account. An Organization admin, Plugin developer or Deploy manager creates one on the Credentials page in Canvas Platform. A service account can hold the Plugin developer and Deploy manager roles. An Organization admin can give it either role; a Plugin developer or Deploy manager can give only the roles they hold, so a Plugin developer cannot create an account that deploys. Its token is shown once and is valid for a year.
Use it in a pipeline. Store the token as a secret in your CI/CD system and expose it to the job as the CANVAS_PLATFORM_TOKEN environment variable. No canvas login step is needed:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: pip install canvas
- run: canvas deploy my-plugin/acme__my_plugin --instance acme-staging --yes
env:
CANVAS_PLATFORM_TOKEN: ${{ secrets.CANVAS_PLATFORM_TOKEN }}
- Check out the full history (
fetch-depth: 0in GitHub Actions).canvas deploypushes the checkout to the plugin’s repository on Canvas Platform, and git refuses to push a shallow clone to a repository that lacks its history. The push to Canvas Platform’s
mainis not forced, so it is refused when Canvas Platform’smainhas a commit the checkout lacks, such as one a developer deployed from their machine without pushing it to the repository the pipeline checks out. Once a pipeline deploys a plugin, deploy it only from the pipeline.- When
CANVAS_PLATFORM_TOKENis set, every command uses it in place of acanvas loginsession, including the git pushescanvas deploymakes. It takes precedence over a session already stored on the machine, so a runner always acts as its service account. --yescommits uncommitted changes without prompting. A deployment that needs consent for another plugin’s custom data exits non-zero and lists the requests, because only a person at a terminal answers those.CANVAS_PLATFORM_URLpoints the job at a platform other than https://platform.canvasmedical.com.
Rotate or revoke it. The token does not refresh. Rotating it on the Credentials page ends the old token, and revoking the service account ends it for good; either way the CLI’s next request says that Canvas Platform did not accept CANVAS_PLATFORM_TOKEN. canvas logout signs out only a browser session and leaves the token alone.
Instance API credentials (credentials.ini): deprecated #
With credentials.ini, the CLI authenticates to each instance with an OAuth client registered on that instance. canvas install, enable, disable, list, config list, logs and the namespace commands authenticate this way today, as do config set, config unset and uninstall for a plugin Canvas Platform does not manage. Keep credentials.ini until the commands you use sign in through Canvas Platform.
A machine can hold both a Canvas Platform sign-in and credentials.ini at once; the CLI picks per command and per plugin, as described in Which plugins go through Canvas Platform.
The file has a section for each Canvas instance subdomain, with that instance’s client_id and client_secret. For example, if your Canvas instance URL is https://buttered-popcorn.canvasmedical.com/, its section is [buttered-popcorn]. Customer Authentication explains how to register a client.
Example:
[buttered-popcorn]
client_id=butter
client_secret=salt
[dev-buttered-popcorn]
client_id=devbutter
client_secret=devsalt
is_default=true
[localhost]
client_id=localclientid
client_secret=localclientsecret
You can define your default host with is_default=true. If no default is explicitly defined, the Canvas CLI will use the first instance in the file as the default for each of the CLI commands.
Deprecation notices #
canvas install prints a deprecation warning on every run. Separately, at most once a day, any command, canvas install included, prints a notice that authentication and deployment are changing, with a link to the migration steps. Both go to standard error, so they do not interfere with piped or redirected output.
Which plugins go through Canvas Platform #
canvas config set, canvas config unset and canvas uninstall work for plugins Canvas Platform manages and for plugins installed straight onto an instance, and each decides per plugin:
- A name without a prefix, or a machine that is not signed in, goes straight to the instance.
- Otherwise the CLI asks Canvas Platform for the plugin. If Canvas Platform has it, the command goes through Canvas Platform; if not, it goes to the instance.
--instance names the target instances for either kind of plugin, and you can repeat it. For a plugin Canvas Platform does not manage, the CLI connects to each --instance directly with its credentials.ini client. --host names a single instance instead, and without either option the command uses the default host from credentials.ini; pass --instance or --host, not both. For a plugin Canvas Platform manages, the CLI refuses --host.
An instance accepts direct writes for every plugin, including one Canvas Platform deployed. A canvas install, config set, config unset or uninstall that goes straight to the instance changes that instance’s copy of the plugin. The next deploy from Canvas Platform installs its own revision over it and replaces the plugin’s variables with the values Canvas Platform stores. Canvas Platform can also deploy over a plugin first installed with canvas install.
git signs in to Canvas Platform’s git server through canvas git-credential, a hidden command that canvas deploy, canvas init and canvas clone register as the repository’s credential helper. It mints a one-hour git token for the plugin named in the repository path.
Update Notifications #
The Canvas CLI automatically checks PyPI for newer versions. If an update is available, a notice is printed to standard error after the command output:
[notice] A newer version of canvas is available (0.112.0 → 0.113.0). Upgrade with: pip install --upgrade canvas
- The check runs at most once every 12 hours; the result is cached locally to avoid unnecessary network requests.
- Because the notice is printed to standard error, it will not interfere with piped or redirected command output.
- To disable update checks, set the environment variable
CANVAS_NO_UPDATE_CHECK=1.
Checking the SDK/runtime version on an instance #
canvas --version reports the version of the locally installed CLI. To find the SDK/runtime version actually running on a given Canvas instance — useful when verifying that an expected SDK change is live — import version from canvas_sdk.handlers.base inside a plugin running on that instance:
from canvas_sdk.handlers.base import version
from logger import log
log.info(f"SDK version: {version}")
Usage #
$ canvas [OPTIONS] COMMAND [ARGS]...
Options:
--version--help: Show this message and exit.
Commands #
login: Sign in to Canvas Platform through your browserlogout: Sign out of Canvas Platformwhoami: Show which Canvas Platform you are signed in to, and as whominit: Create a new plugindeploy: Publish a plugin to Canvas Platform and deploy itclone: Clone a plugin’s repository from Canvas Platformuninstall: Uninstall a pluginenable: Enable a plugin from a Canvas instancedisable: Disable a plugin from a Canvas instancelist: List all plugins from a Canvas instancevalidate: Validate a plugin’s manifest and that all handlers load in the sandboxvalidate-manifest: Validate the Canvas Manifest json filelogs: Listen and print log streams from a Canvas instanceconfig list: List plugin variables on a Canvas instanceconfig set: Set plugin variablesconfig unset: Clear plugin variablesinstall: Install a plugin into a Canvas instance (deprecated; usedeploy)
canvas login #
Sign in to Canvas Platform through your browser (OAuth authorization code with PKCE). The sign-in URL is also printed, for a machine without a browser. See Signing in to Canvas Platform.
Usage:
$ canvas login [OPTIONS]
Options:
--platform TEXT: Canvas Platform URL. Defaults to$CANVAS_PLATFORM_URL, the platform you last signed in to, or https://platform.canvasmedical.com--help: Show this message and exit.
canvas logout #
Revoke this machine’s Canvas Platform session and delete its stored tokens. A service account token in CANVAS_PLATFORM_TOKEN is left alone, and the command says that commands still act as that account.
Usage:
$ canvas logout [OPTIONS]
Options:
--platform TEXT: Canvas Platform URL--help: Show this message and exit.
canvas whoami #
Show the Canvas Platform the CLI uses and where that choice came from (--platform, CANVAS_PLATFORM_URL, your last canvas login, or the default), then the account it signs in as, whether the credential is a browser session or a service account token, and each organization’s plugin prefix and what you can do there. When CANVAS_PLATFORM_TOKEN is set alongside a stored session, it says the token takes precedence.
whoami asks Canvas Platform, so a revoked or expired credential is reported rather than shown as signed in. It never prints a token, so it is safe to run in a CI job’s log. It exits non-zero when you are not signed in, with the canvas login command to run.
Usage:
$ canvas whoami [OPTIONS]
Options:
--platform TEXT: Canvas Platform URL--help: Show this message and exit.
canvas init #
Create a new plugin. Signed in to Canvas Platform, the package is named <org prefix>__<package>, registered with Canvas Platform, and given a git repository whose origin is Canvas Platform, ready for canvas deploy. Signed out, it is named from the project name alone and init says so: canvas deploy refuses such a plugin, so run canvas login and canvas init again for one you can deploy. Run canvas whoami first if you are unsure which platform you are signed in to.
Usage:
$ canvas init [OPTIONS] [PLUGIN_TYPE]
Arguments:
PLUGIN_TYPE:handler(default) orapplication
Options:
--org TEXT: Organization that publishes the plugin, when you can publish in several. Without it,initasks.--help: Show this message and exit.
canvas deploy #
Publish a plugin’s code to Canvas Platform and deploy it to one or more instances.
Usage:
$ canvas deploy [OPTIONS] PLUGIN_DIR
Arguments:
PLUGIN_DIR: Path to the plugin package, the folder that holdsCANVAS_MANIFEST.json[required]
Options:
--instance TEXT: Instance slug to deploy to, repeatable. Without it,deploytargets the only instance you can deploy to, and otherwise lists the choices.--ref TEXT: Deploy a pushed branch, tag or commit as-is, without pushing.--no-push: Deploy the pushedmainas-is, without pushing.--push-only: Push HEAD tomainwithout deploying it to any instance, so the plugin’s code and its catalog listing are updated on Canvas Platform. Takes no--instance,--refor--no-push.-y, --yes: Commit uncommitted changes without prompting, with the default message. Does not approve consent requests.--help: Show this message and exit.
Example:
$ canvas deploy paperwork-eviscerator/acme__paperwork_eviscerator --instance acme-staging
Notes:
What deploy does
- Checks the manifest
nameis publisher-prefixed and the package folder has the same name, before anything is committed or pushed. Then registers the plugin with Canvas Platform under the organization its prefix names. - Points the repository’s
originat Canvas Platform, withcanvasas git’s credential helper for it. - Commits uncommitted changes after you confirm. Without a terminal to confirm at, it refuses uncommitted changes unless you pass
--yes. - Pushes HEAD to
main, deploys that commit to each instance, and waits for each instance’s outcome.
The command exits non-zero unless every instance succeeds, so it can gate a CI job.
Waiting for the outcome
For a plugin Canvas Platform manages, canvas deploy, canvas config set, canvas config unset and canvas uninstall each create a deployment, wait for it to finish, and print each instance’s outcome. They stop waiting after 300 seconds, print a message such as Deploy of acme__intake is still running after 300s (deployment <id>)., and exit non-zero. The CLI does not cancel the deployment when it stops waiting.
Canvas Platform sends a plugin only the stored values whose keys the deployed revision’s manifest declares in variables. When it leaves stored values out, the CLI lists them under that instance’s outcome:
acme__intake on acme-staging: succeeded
Stored values left out because this revision does not declare them: OLD_FLAG
If the plugin still needs one of these values, declare its key in the manifest and deploy again.
Plugin repository layout
Canvas Platform hosts one git repository per plugin, rooted at the directory that contains the package:
paperwork-eviscerator/ # repository root
├── acme__paperwork_eviscerator/
│ ├── CANVAS_MANIFEST.json
│ ├── OVERVIEW.md # the plugin's overview in the catalog
│ └── handlers/
├── pyproject.toml
└── tests/
canvas init creates this layout. If the package is not in a git repository yet, deploy offers to create one at its parent directory. A package nested deeper inside a larger repository is refused; move it into its own repository, or canvas clone it.
Because deploy pushes a git commit, .gitignore decides what reaches Canvas Platform. Keep virtual environments, node_modules and local secrets out of the repository with it.
Consent for cross-plugin data access
A plugin that reads another plugin’s custom data needs consent before it deploys. At a terminal, deploy shows each consent request and asks you to answer it. Under --yes, or without a terminal, it lists the requests with the deployment id and exits non-zero without answering them.
Validate first
Run canvas validate before you deploy. It runs the manifest checks, the static lint, and sandbox-load validation locally, where errors are faster to fix than after a push.
canvas clone #
Clone a plugin’s repository from Canvas Platform with origin and the credential helper set, ready for canvas deploy.
Usage:
$ canvas clone [OPTIONS] NAME [DIRECTORY]
Arguments:
NAME: Plugin name, e.g.acme__intake[required]DIRECTORY: Where to clone it, a directory named after the plugin by default
Options:
--help: Show this message and exit.
canvas install #
Install a plugin into a Canvas instance.
Usage:
$ canvas install [OPTIONS] PLUGIN_NAME
Arguments:
PLUGIN_NAME: Path to plugin to install [required]
Options:
--variable TEXT: Non-sensitive variables to set, e.g. Key=value--secret TEXT: Sensitive variables to set (treated as sensitive=true), e.g. Key=value--enable / --disable: Install the plugin in an enabled or disabled state. Defaults to--enable.--host TEXT: Canvas instance to connect to--help: Show this message and exit.
Notes:
Where to run it
Run it from one directory above the plugin folder, the one that holds
CANVAS_MANIFEST.json, and pass the folder name. If the manifest is atextensions/encounter_list/CANVAS_MANIFEST.json, run this fromextensions:$ canvas install encounter_list --host your-host- Or run
canvas install .from inside the plugin folder. - The command is
canvas install. There is nocanvas plugin install.
Plugin folder layout
- Keep the manifest, the package’s
__init__.py, and the folders it references (such asroutes/andprotocols/) in the same directory. Class paths in the manifest, such asmy_plugin.routes.charting, resolve from there. - An extra level of nesting, with the manifest outside the inner package folder, makes those paths resolve one level too deep. The plugin loads, but its SimpleAPI endpoints return an empty 404, often with nothing in
canvas logs. If that happens, check the folder structure and that every import in the referenced modules resolves.
Validation before upload
canvas install runs the same checks as canvas validate before it uploads anything:
- Manifest validation (schema, tags, handler resolution)
- Static lint (scans your source for sandbox-forbidden constructs and Custom Data mistakes)
- Sandbox-load validation (imports every handler in the sandbox)
If the static lint reports an error, or a handler fails to load (for example, because of a disallowed import like subprocess), the install stops before the plugin is built or uploaded. Run canvas validate first for detailed per-handler results.
What gets packaged
The CLI packages every file in the plugin folder except:
__pycache__directories*.pycand*.pyofilesnode_modulesdirectories- Hidden files and directories (for example,
.gitand.env) - Symlinks
To exclude more files, add a .canvasignore file:
- Put it in the directory you run
canvas installfrom. The CLI reads it from the current working directory, not from the plugin folder. - It uses .gitignore syntax, and its patterns match paths relative to the plugin folder.
# Exclude test files
test_*.py
The CLI prints a warning, but still installs, when a single file is over 1 MB, the package holds more than 100 files, or its total size is over 1 MB.
Troubleshooting
- The install fails with an HTTP 500: the package usually has too many files, most often because a Python virtual environment was created inside the plugin folder. Rename the folder so it starts with a dot (for example,
.canvas-env), since hidden folders aren’t packaged, or list it in.canvasignore.
canvas uninstall #
Uninstall a plugin. A plugin Canvas Platform manages is removed by an uninstall deployment from each --instance, which is required. Any other plugin is removed from the instance directly, using credentials.ini (deprecated; see Instance API credentials).
Usage:
$ canvas uninstall [OPTIONS] NAME
Arguments:
NAME: Plugin name to uninstall [required]
Options:
--instance TEXT: Instance slug to uninstall from, repeatable--host TEXT: Canvas instance to connect to, for a plugin Canvas Platform does not manage--force: Uninstall an enabled plugin from an instance directly--help: Show this message and exit.
canvas enable #
Enable a plugin from a Canvas instance..
Usage:
$ canvas enable [OPTIONS] NAME
Arguments:
NAME: Plugin name to enable [required]
Options:
--host TEXT: Canvas instance to connect to--help: Show this message and exit.
canvas disable #
Disable a plugin from a Canvas instance..
Usage:
$ canvas disable [OPTIONS] NAME
Arguments:
NAME: Plugin name to disable [required]
Options:
--host TEXT: Canvas instance to connect to--help: Show this message and exit.
canvas list #
List all plugins on a Canvas instance.
Usage:
$ canvas list [OPTIONS]
Options:
--host TEXT: Canvas instance to connect to--help: Show this message and exit.
canvas validate #
Validate a plugin’s manifest and that all handlers load in the sandbox.
Usage:
$ canvas validate [OPTIONS] PLUGIN_NAME
Arguments:
PLUGIN_NAME: Path to plugin to validate [required]
Options:
--help: Show this message and exit.
This command runs full pre-flight validation combining:
- Manifest validation — Schema checks, tag validation, handler resolution, and unreferenced handler warnings (everything
validate-manifestdoes). - Static lint — Scans the plugin’s source for sandbox-forbidden constructs and Custom Data mistakes before any code runs.
- Sandbox-load validation — Imports every handler the way the plugin runner will, catching violations that would otherwise surface only at runtime on the instance.
Static lint #
Before it loads any handlers, canvas validate scans every .py file in the plugin — skipping directories like tests, build, and dist within the plugin — for patterns that compile cleanly but fail, or silently misbehave, once your code runs on the instance. Each finding is reported with a rule code in brackets. Warnings are printed but do not block validation; any error fails the command and exits with code 1.
$ canvas validate my_plugin
⚠ my_plugin/handlers/protocol.py:42 [custom-model-id-vs-dbid] Widget.objects.filter(id=…) — CustomModels use `dbid` as their primary key (only core SDK models have `id`). Use `dbid=…` instead.
These issues will fail on the instance (sandbox / Custom Data):
✗ my_plugin/handlers/protocol.py:18 [type-blocked] `type()` takes one argument in the sandbox. Declare classes with `class … :` rather than `type(name, bases, dict)`.
Sandbox constructs (errors). These compile under RestrictedPython but are rejected when a handler runs on the instance, so a plain sandbox load can miss them. See Sandboxing and Allowed Imports for the full list and the allowed alternatives.
| Rule code | Flags |
|---|---|
type-blocked | A type() call with anything other than a single argument, such as type(name, bases, dict). The sandbox’s type takes one argument, so declare classes with class …: instead. type(x) is not flagged. |
augmented-subscript | Augmented assignment on a subscript, e.g. d[k] += v — rewrite as d[k] = d[k] + v |
augmented-attribute | Augmented assignment on an attribute, e.g. obj.attr += v — rewrite as obj.attr = obj.attr + v |
@dataclass(frozen=True) and @dataclass(slots=True) load and run fine in the sandbox and are intentionally not flagged.
Custom Data (errors). Both leave tables silently uncreated, so queries fail at runtime. See Custom Models and the Quick Start for setup.
| Rule code | Flags |
|---|---|
custom-model-wrong-dir | A CustomModel subclass defined outside <plugin>/models/ — Canvas only loads models from that directory |
missing-custom-data-block | CustomModels are present but the manifest has no custom_data block (an empty block counts as missing — it must be non-empty) |
Custom Data (warnings). These don’t block validation but usually indicate a bug:
| Rule code | Flags |
|---|---|
custom-model-id-vs-dbid | .filter(id=…) / .get(id=…) on a local CustomModel — CustomModels key on dbid, not id. Use dbid=… |
lazy-fk-string-ref | A ForeignKey/OneToOneField/ManyToManyField with a string reference to a CustomModel defined in this plugin — import the class and pass it directly |
Sandbox-load validation #
After the static lint passes, sandbox-load validation executes each handler module in the plugin sandbox to catch:
- Disallowed imports — Modules like
subprocess,socket, orosthat are blocked by the sandbox. - RestrictedPython compile-time errors — Syntax or constructs that RestrictedPython cannot compile.
- Import errors — Missing dependencies or broken imports.
- Refused code in helper modules — Functions in the modules your handlers import that contain code the sandbox refuses. Every handler fails with a
PluginInstallationErrorlisting each construct by module and line. See Helper modules.
For each handler, the output shows whether it loaded successfully:
$ canvas validate my_plugin
Loading 2 handler(s) in the sandbox:
✓ my_plugin.handlers.events:MyHandler
✗ my_plugin.handlers.api:APIHandler
ImportError: 'subprocess' is not an allowed import
1 of 2 handler(s) failed to load in the sandbox.
The command exits with code 1 if any handler fails validation.
If your handlers read attributes that don’t exist while they load, the output ends with a warning that lists them. In the sandbox those reads return None for now, and a future release will raise AttributeError instead (see Attributes that don’t exist). The warning doesn’t fail validation:
$ canvas validate my_plugin
Loading 1 handler(s) in the sandbox:
✓ my_plugin.handlers.events:MyHandler
Warning: these names do not exist. Each reads as None today and will raise an AttributeError in a future release:
- Settings.retry_limit
Limitations #
A passing canvas validate confirms that handlers import cleanly under the sandbox — it does not guarantee the plugin is fully sandbox-clean. RestrictedPython checks attribute and item access inside compute() at request time, not at import time, so violations during handler execution won’t be caught by this command.
canvas validate-manifest #
Validate the Canvas Manifest json file.
Usage:
$ canvas validate-manifest [OPTIONS] PLUGIN_NAME
Arguments:
PLUGIN_NAME: Path to plugin to validate [required]
Options:
--help: Show this message and exit.
Validations performed:
- Schema validation — Checks that
CANVAS_MANIFEST.jsoncontains all required fields and valid values. - Handler resolution — Verifies that every handler class declared in the manifest (
protocols,applications, andhandlers) resolves to a file the plugin runner can find at runtime.
Handler resolution and directory layout #
The plugin runner loads handlers by mapping dotted module paths to files relative to the plugin’s install directory. For a plugin named my_plugin with a handler class my_plugin.handlers.events:MyHandler, the runner expects handlers/events.py inside the plugin directory — the directory containing CANVAS_MANIFEST.json.
A common mistake is placing CANVAS_MANIFEST.json in a parent directory above the plugin package. This passes schema validation and works locally, but fails at runtime with ModuleNotFoundError — the handler files are nested one level too deep.
Correct layout:
my_plugin/
├── CANVAS_MANIFEST.json # ← manifest inside the package
├── handlers/
│ └── events.py
└── ...
Incorrect layout:
project/
├── CANVAS_MANIFEST.json # ← manifest above the package (wrong!)
└── my_plugin/
└── handlers/
└── events.py
If validate-manifest detects handlers that won’t resolve, it reports which classes are affected and the file paths the runner expects:
Error: these handler classes won't be found by the plugin runner with the current directory layout:
- my_plugin.handlers.events:MyHandler
runner expects: my_plugin/handlers/events.py
CANVAS_MANIFEST.json must live inside the plugin's package directory (the directory whose name matches the manifest "name"), alongside the handler packages — not in a parent directory above them.
canvas logs #
Subscribes to a log stream and prints to your console. Optionally fetches historical logs first.
Usage:
$ canvas logs [OPTIONS]
Options:
--host TEXT: Canvas instance to connect to--help: Show this message and exit.--since TEXT: Lookback window (e.g. ‘24h’, ‘2h30m’). Mutually exclusive with –start/–end.--start TEXT: Start time (ISO/RFC3339) or ‘now’.--end TEXT: End time (ISO/RFC3339) or ‘now’. Defaults to now if start is provided.--no-follow: Historical only; do not stream live logs.--level TEXT: Repeatable. –level ERROR –level WARN--source TEXT: Filter by source/service.--plugin TEXT: Repeatable. –plugin foo –plugin bar.--handler TEXT: Repeatable. Qualified handler name (e.g. my_plugin.handlers.Foo).--page-size INTEGER: Fetch size per page (historical). [default: 200]--limit INTEGER: Max historical logs to print.--all: Fetch all pages until exhausted (historical).--interactive: After each page, prompt to load more.--cursor TEXT: Resume token from a previous run.--help: Show this message and exit.
canvas config list #
List plugin variables on a Canvas instance. Each variable is rendered as [set] or [not set], with a (sensitive) annotation for sensitive variables. Values themselves are never displayed — to read a value, use the Django Admin UI (gated by managing-user permissions).
Usage:
$ canvas config list [OPTIONS] PLUGIN
Example output:
$ canvas config list my_plugin
API_TOKEN [set] (sensitive)
LOG_LEVEL [not set]
Arguments:
PLUGIN: Plugin name to list variables for
Options:
--host TEXT: Canvas instance to connect to--help: Show this message and exit.
Example Output:
$ canvas config list my_plugin
API_TOKEN = [set] (sensitive)
WEBHOOK_URL = [set]
DEBUG_MODE = [not set]
canvas config set #
Set (or update) one or more plugin variables. Pass one or more KEY=value pairs as positional arguments.
For a plugin Canvas Platform manages, the values are stored in Canvas Platform and a configure deployment brings them to the running plugin on each --instance. Without --instance, the only instance the plugin is installed on that you can configure is targeted. Values are kept per instance, and a later canvas deploy carries them forward for the keys that revision declares; see Waiting for the outcome.
For a plugin Canvas Platform does not manage, the values are written to the instance directly with credentials.ini (deprecated; see Instance API credentials).
Usage:
$ canvas config set [OPTIONS] PLUGIN [KEY=VALUE]...
Examples:
Set a single variable:
$ canvas config set acme__my_plugin API_TOKEN=your_api_token_value --instance acme-staging
Set multiple variables in one call:
$ canvas config set acme__my_plugin API_TOKEN=abc123 LOG_LEVEL=info --instance acme-staging
Set a variable whose value is a list with one entry per line — for example a redirect allowlist (see the Redirect effect). The value is newline-delimited (not comma-separated), so use your shell’s newline quoting to preserve the line breaks. In bash/zsh, ANSI-C quoting ($'…') turns \n into a real newline:
$ canvas config set acme__my_plugin $'REDIRECT_ALLOWLIST_INTERNAL=/panel\n/patient' --instance acme-staging
Arguments:
PLUGIN: Plugin name to set variables forKEY=VALUE...: Variables to set, keeping each one’s declared sensitivity
Options:
--instance TEXT: Instance slug to configure, repeatable--secret KEY=VALUE: Set a sensitive value for a key no revision of the manifest declares, repeatable--variable KEY=VALUE: Set a non-sensitive value for a key no revision of the manifest declares, repeatable--host TEXT: Canvas instance to connect to, for a plugin Canvas Platform does not manage--help: Show this message and exit.
Whether each value is treated as sensitive is determined by the plugin’s
CANVAS_MANIFEST.json(variables: [{name, sensitive}]) —canvas config setdoes not change the sensitive flag of a declared variable.
canvas config unset #
Clear one or more plugin variables. For a plugin Canvas Platform manages, the values are cleared in Canvas Platform and a configure deployment removes them from the running plugin. For any other plugin, each value is set to empty on the instance directly.
Usage:
$ canvas config unset [OPTIONS] PLUGIN KEY...
Arguments:
PLUGIN: Plugin name to clear variables forKEY...: Variable keys to clear, e.g.API_KEY
Options:
--instance TEXT: Instance slug to configure, repeatable--host TEXT: Canvas instance to connect to, for a plugin Canvas Platform does not manage--help: Show this message and exit.