@incorta/appkit
v1.3.1
Published
The Incorta Apps CLI — create, develop, and deploy Incorta apps from your terminal.
Readme
Incorta AppKit
The command line for Incorta Apps. Create an app, develop it against your real tenant data, and put it in production — without leaving your terminal.
$ npm install -g @incorta/appkit
$ appkit create --name "Sales Dashboard"
$ cd sales-dashboard && appkit dev
✓ .env refreshed (9 values)
Starting the tanstack dev server → http://localhost:3000That localhost:3000 is not a mock. It signs you in through Incorta and
queries live tenant data, as you — from the very first run.
Why AppKit
- Real data on localhost. Every app is born with its own Incorta sign-in
wired up.
appkit devstarts your app authenticated against your tenant — no credential copy-paste, no fake fixtures. - Every push is a version.
appkit pushturns your commits into a platform version — the same history the web builder's agent works on — andappkit open --previewputs it in front of you in the builder, where any version previews on demand. - Production is a decision, not a script.
appkit publishbuilds a version and puts it live on its stable address. Rolling back isappkit publish --version 3— no code changes; the platform builds that version again and flips. - You and the AI agent, same workspace. Apps edited in the Incorta Apps
web chat and apps edited in your editor share one version history.
appkit pullandappkit pushkeep both sides moving. - Everything scripts. Every prompt has a flag, every command has
--json. If you can type it, you can automate it.
Install
npm install -g @incorta/appkit # or one-off: npx @incorta/appkitNeeds Node.js 22+ and git. Runs natively on macOS, Linux, and Windows (on Windows, the standard Git for Windows install is all you need — no WSL).
Sign in once
Paste the same URL you use in the browser — tenant and environment are understood automatically, and your browser finishes the SSO:
appkit login 'https://apps.example.com/incorta/!mytenant'Quote the URL: in an interactive zsh or bash, a bare !mytenant is history
expansion and the shell fails with event not found before appkit ever
runs. If quoting is a nuisance, name the tenant separately instead:
appkit login https://apps.example.com/incorta --tenant mytenantFrom here on, every command authenticates by itself and refreshes its own
session. appkit whoami tells you who you are and where; appkit logout
signs you out.
Create an app
appkit create # prompts for name, description, and stackOne command, and the app exists on the platform — template-seeded, sign-in provisioned — and is cloned into a linked folder on your machine, dev credentials already in place.
Already have apps? Bring one down anywhere:
appkit clone "Sales Dashboard" # by name or id — a fresh laptop is
# running it two commands laterDevelop with your real data
appkit devYour stack's dev server, on your machine, signed in through Incorta. Every
start quietly refreshes the app's .env (secrets + sign-in credentials), so
rotated credentials and new secrets heal themselves — restart and go.
Build apps with any coding agent
appkit create and appkit clone don't just hand you a folder — they can
wire a skill (Agent Skills — SKILL.md + reference docs) straight into
your coding agent's config, so the agent knows the app's platform contracts
(locked files, the auth pattern, secrets, the database workflow) before it
writes a line of code:
appkit create --name "Sales Dashboard" --agent claude-codeTwo skills install by default, no flag needed: appkit-cli (this CLI —
every command, and the per-stack coding conventions for the app it just
created) and incorta-data-discovery (how to read the tenant's data safely
from app code). --agent only needs naming once your machine has more than
one coding agent installed, or when running headless; on an interactive
terminal with exactly one agent detected, it's picked for you.
Want more? Add any of the optional, stack-specific skills (dash,
react-best-practices, ui-ux-pro-max) with --skill, repeatable:
appkit create --name "Sales Dashboard" --stack dash --skill dashAlready have a project? Install or refresh skills into it any time, without touching the app itself:
appkit skills list # what's installed, and what's available to add
appkit skills install --skill react-best-practices --agent cursor
appkit skills install --skill appkit-cli --agent claude-code --scope global # once for every project
appkit skills update --all # bring installed skills up to their bundled version--agent accepts any of these ids — whichever CLI or config directory is
actually present on your machine is what appkit detects and offers:
| id | Agent |
|---|---|
| claude-code | Claude Code |
| cursor | Cursor |
| codex | Codex CLI |
| opencode | OpenCode |
| github-copilot | GitHub Copilot |
| gemini-cli | Gemini CLI |
| antigravity | Antigravity |
| pi | Pi |
| goose | Goose |
| hermes | Hermes Agent |
--scope global (the default) installs into your home directory, shared
across every project that agent opens; --scope project installs into this
repo's own agent config instead, checked in alongside the app if you commit
it.
Using it in the agent
Once appkit-cli is installed, open the agent inside the app's folder and
make your first message /appkit-cli — that loads the skill into the
conversation so the agent has the full command reference and this stack's
coding conventions in context before it touches anything. After that, just
ask for what you want in plain language — the agent reaches for the skill
on its own from here on:
> /appkit-cli create a sales dashboard appor
> /appkit-cli
Skill loaded: appkit-cli — the appkit CLI reference and this project's
(tanstack) coding conventions are now in context.
> create sales dashboardYou only need /appkit-cli once per conversation — a new chat with the
agent means loading it again the same way. It doesn't have to be its own
message either, as the first example shows — put the whole request on the
same line and the agent loads the skill first, then acts on it.
Preview in the builder
appkit push # your commits become a version
appkit open # the builder — or the live app, once publishedPushing 1 commit…
✓ pushed 1 commit → version 4 (4312be0)Previews are not stored any more: the builder builds the version you look
at on demand (a cold build takes a minute or three; a warm reopen is
instant) and layers the live dev server on top. There is no shareable
preview link — a preview is for the people editing the app; the address
you send around is the published one. Working tree not committed yet?
appkit push --commit -m "wip" handles that too.
Go live — and roll back fearlessly
appkit publish # first time asks for the subdomain
appkit open --live # see what the world seesPublishing pins your app to its stable address. Later pushes and previews never touch it — production moves when you say so:
appkit publish --version 3 # production rollback: builds version 3 again, then flips
appkit unpublish # take it offline (the stored build goes with it)Production keeps its own secrets across all of this: the first publish seeds them from the development set, and no later publish — rollback or unpublish included — overwrites them. See Production has its own set.
What happens when you publish
Publishing is a run on the platform: build → security check →
production database → schema plan → expand → flip the live pointer →
contract. The run's first step builds the version in a one-off pod:
the platform keeps exactly one stored build per app — the published one —
so the security check probes and scans the exact artifact that will be
served, and the previous build is deleted once the new one is live. A
republish of the version already built skips the step; a rollback builds
the older version again. appkit publish follows the run, prints each
step as it settles (a cold build takes a minute or three) and ends on the
live URL. Additive schema changes and code-only republishes never pause.
The run pauses for your confirmation — twice at most — only when there is something to acknowledge, each pause with its own review:
- After the security check, when a finding needs a yes: a dependency carrying a critical/high advisory, or the code analysis seeing the platform's auth gate weakened in source. Nothing has been provisioned or changed yet.
- After the schema plan, when production data could be lost or rejected — a table or column drop, a narrowing type, a new NOT NULL / UNIQUE / FK / CHECK, an enum losing values.
appkit publish shows each review as it comes: one sentence per item
(the rows a change touches; where a finding is and what to do about it),
the check's other findings grouped by severity, and (with --show-sql)
the exact statements the plan will run. You answer y/N per item; any
"no" cancels the run and nothing changes in production. A clean check and
an additive plan pause nowhere.
! Publishing version 4 needs your confirmation — the security check found findings to acknowledge:
Dependency advisories
1. CRITICAL: [email protected] — GHSA-…, fixed in 4.17.21
Code findings
2. PUBLIC_PATHS widened beyond the template
[code-sast] public-paths-widened src/_server.py:12
Keep PUBLIC_PATHS as the template defines it; gate new routes behind sign-in.
? CRITICAL: [email protected] — GHSA-…, fixed in 4.17.21 (y/N) y
? PUBLIC_PATHS widened beyond the template (y/N) y
Publishing version 4 (run pub-8kbuwrtinmedz7a3s2co5)
✓ Build (1m 12s)
✓ Security check (2.1s) 3 warnings
✓ Provision production database (0.4s)
⏸ Plan production schema — awaiting confirmation
! Publishing version 4 needs your confirmation — production data can be lost or rejected:
Schema changes
1. Column `notes.body` will be dropped — 88 rows
2. Apply 1 other production schema statement
? Column `notes.body` will be dropped — 88 rows (y/N)Headless runs (CI, pipes, --json) never prompt. They acknowledge with
flags — one per review family, so a script names exactly what it is
willing to accept — and fail with exit code 2, naming the flags they lack,
when they would otherwise have to ask:
appkit publish --accept-advisories # security pause: every dependency advisory
appkit publish --accept-code-findings # security pause: every code-analysis finding
appkit publish --accept-data-loss # schema pause: every destructive changeBecause a run can pause twice, pass every flag you are prepared to accept
up front: a headless run that clears the security pause with
--accept-advisories alone is refused again at the schema pause when the
plan loses data, and stays parked there exactly like the first — the
refusal's hint spells out the exact command either time. A review item of
a family the CLI has no flag for is refused headless by name — appkit
publish --confirm <runId> on a terminal, or the web app, is the way in.
A refused headless publish leaves the run parked for a while: resume it from any machine — or after reading the review in the web app's Publishing section — with its id:
appkit status # shows the parked run and its id
appkit publish --confirm <runId> # review + confirm it, then follow itCtrl-C at a y/N is not a "no": the run stays parked (exit 130) and the
message names the --confirm that resumes it.
--no-wait returns as soon as the run is confirmed; appkit status
follows its steps — and if the schema plan parks it for its second
review, appkit publish --confirm <runId> resumes it. A run that fails
prints the failing step, its error code and the platform's hint (a taken
subdomain, a constraint the data does not satisfy, …) and exits 1. A build
that fails (BUILD_FAILED, or BUILD_TIMEOUT past the build budget) also
prints the last lines of the build output, indented under the verdict —
fix the error, or ask the assistant in the builder, and publish again.
Destructive statements execute only after the new version is live, so
a failed contract never takes production down.
The command follows the run wherever it stands: a run the platform hands
back still before its plan is watched until it either finishes or pauses —
and a pause is reviewed right there, prompts or flags as above. If the
connection drops while the platform holds the publish request, appkit
publish picks the run up from appkit status instead of reporting a
connectivity failure for a publish that may already be live.
Before anything goes live
The platform's security check runs first, on the exact version being published. Five families:
| Family | Looks for | Outcome |
|---|---|---|
| Secrets in the tree | Hardcoded credentials in the committed files — including the app's own secret values and a committed .env | blocks |
| Secrets in the built client | The app's secret values leaking into the browser bundle | blocks |
| Live auth-gate probe | The freshly built version, served in a probe pod, answering without a session where the stack can prove a gate | blocks where provable, warns elsewhere (Streamlit gates in-script) |
| Dependency advisories | Known-malicious and vulnerable packages in the lockfile | malware blocks; critical/high need acknowledgement; moderate/low and a stale corpus warn |
| Code analysis | Semgrep-powered static analysis of the committed source with Incorta's own rules: the platform's auth gate weakened in source (a widened PUBLIC_PATHS, an inline public_paths, a gate() wrapped in a condition, a discarded auth verdict), plus dangerous shapes — eval/exec on input, unsafe deserialisation, shell commands built from strings, TLS verification off, CORS * with credentials, computed dangerouslySetInnerHTML | gate weakenings need acknowledgement on every publish (never remembered — the same line could widen further); dangerous shapes warn; never blocks, a pattern match is not a proof |
A blocked publish lists every finding with its rule, file and line — never the matched value — and the rule's remedy where it has one; nothing is provisioned or changed. Warnings never pause a publish; they are counted on the run and shown in the review.
Stay in sync
The web chat agent and your editor move the same workspace. Three commands keep you oriented:
appkit status # where you stand: drift, versions, agent activity
appkit pull # bring down new workspace commits
appkit push # your commits become platform versionsEvery push mints a version you can see in the web UI. If the workspace moved
while you worked, push says so — appkit pull, then push again.
Watch it run
appkit logs # the recent window of output, oldest → newest
appkit logs --follow # live tail — Ctrl-C stops
appkit logs --version 3 --stream stderr
appkit logs --json | jq .msgDev runs, previews, and the published app all land in one stream, every line stamped and labeled with its source. Nothing is guessed or rewritten — you read exactly what the app wrote.
Configure with secrets
Secrets live encrypted on the platform and reach your app as env vars — at
dev time and in previews from the development set, in production from
its own set. Values go in; they never come
back out:
appkit secrets set API_KEY # hidden prompt
appkit secrets set --file .env.production # bulk import
appkit secrets list # names + timestamps only
appkit secrets rm API_KEYFor local runs, appkit env pull writes the full runtime env into .env —
the same values your app gets in the cloud, issued for localhost. The file
is a regenerable projection (and is kept out of git for you): the platform
stays the single source of truth.
These are the development secrets: dev runs, previews, and .env
read them, and a change is live for them at once.
Production has its own set
The published app runs its own secret set. The first publish seeds it from the development secrets; from then on the two are independent — a publish never overwrites production, and changing a development secret never touches the live app:
appkit secrets list --production # what production runs, and what waits
appkit secrets set --production API_KEY # save + apply: the live app restarts on it
appkit secrets set --production --no-apply --file .env.prod # save only
appkit secrets apply # everything saved, one restart
appkit secrets add-to-production STRIPE_KEY # copy a development secret production lacks
appkit secrets rm --production OLD_KEY # gone, and the live app restarts nowA production change is saved until it is applied: applying restarts the
published app so it starts on the whole set (visitors see one reload).
set --production applies at once unless --no-apply; rm --production
always does — a removed value cannot linger — and with several names it
restarts once per name. Should a restart not go through, the set already
counts as applied, so appkit secrets apply --restart is the retry: it
restarts the published app on its current set. While changes wait,
appkit status shows a Secrets row and appkit secrets list --production
marks them not applied; the list also names development secrets that
production lacks (add-to-production copies them, values included, without
ever showing one). DATABASE_URL (once the app has a production database)
and any other platform-injected value are listed as system, never
editable; the platform's INCORTA_* sign-in env is injected too but not
listed. The set is managed only while the app is published — it survives
an unpublish and is back with the next publish.
Server-side apps can also receive a platform-managed PostgreSQL database as
DATABASE_URL. When external access is ready, create, clone, dev, and
env pull put its TLS URL in .env and report only ready—the URL and
password are never printed, including with --json. An unavailable public
endpoint does not affect hosted development, previews, or the published app,
which keep using the private database path.
The v1 database credential is shared by every editor of the app. Removing one editor's catalog access does not invalidate a URL they already copied; rotate the database credential in the Apps Database panel to disconnect all sessions and revoke the old URL for everyone.
Pass the generated URL to your driver unchanged: it contains
sslmode=verify-full (and a CA path when the deployment needs one). Do not
disable hostname verification or replace it with sslmode=require. Use one
small process-wide pool—currently at most two local sessions per process—and
close it on shutdown:
import { Pool } from 'pg';
const pool = new Pool({ connectionString: process.env.DATABASE_URL, max: 2 });import os, psycopg
connection = psycopg.connect(os.environ["DATABASE_URL"], connect_timeout=10)The app role has a shared total connection ceiling that also protects capacity
for hosted runtimes. If a driver reports too many clients, 53300, or
08P01, close stale appkit dev/watch processes and reduce the pool; do not
open one pool per request. unavailable means the app or deployment is not
enrolled or the public path is unhealthy—hosted development, previews, and
the published app remain on the private path.
Keep the database schema in db/schema.ts, then use the same managed workflow
on a laptop and in the hosted workspace:
appkit env pull # refresh the development DATABASE_URL in .env
appkit db push # apply db/schema.ts and refresh generated bindings
appkit db types # refresh bindings only
appkit push # a version — the builder previews it on the shared development database
appkit publish # apply the committed snapshot to production, then go livedb push prints the SQL it runs. Renaming a table or column without changing
its body (type, constraints, default) keeps the data and is reported as a
RENAME: line. Renaming and changing a column in the same push, or a rename
the heuristic cannot pair unambiguously (two candidates with the same body), is
applied as DROP + CREATE — read the DATA LOSS lines before relying on it, and
split such changes into two pushes when the rows matter.
Commit both db/schema.ts and db/snapshot.json. push warns when the
snapshot is missing or older than the schema; publishing refuses that
commit so production is never planned from an ambiguous schema. Development
schema changes are immediate and disposable. Production changes are planned
separately and may require confirmation before destructive work is applied.
appkit revert <n> also syncs the development database to that version's
committed schema — data added by later schema changes is dropped — and reports
the outcome (the same DATA LOSS: / SQL: lines as db push).
After rotation or gateway recovery, stop the local process, run
appkit env pull, and restart. Keep transactions short. If a connection dies
while COMMIT is in progress, do not blindly replay it: reconnect and use the
application's idempotency key or read model to determine whether it committed.
For support, share the timestamp, driver version, status, and redacted error
code—never the URL, .env, SQL/bind values, or password.
Read your Incorta data
The commands above build and ship apps. schemas, tables and query read
the analytics side of the same environment — schemas, tables, and rows — as
you, so every answer is filtered to what your Incorta account may see:
appkit schemas list --type business
appkit schemas get Sales
appkit tables get Sales orders
appkit tables get Sales.orders # same thing, however you have it to hand
appkit query '{"measures":["HR_BS.Employee_BS.SALARY"],"rows":["HR_BS.Employee_BS.DEPT"],"aggregate":true}'schemas and tables answer what exists; query is the one that returns
rows. Discovery first is not just politeness — query needs real
SCHEMA.VIEW.COLUMN names, drawn from one business view.
Everything pages, and every command takes --json:
| Command | Default page | Max |
|---|---|---|
| schemas list | 50 schemas | 100 |
| schemas get | 50 objects | 100 |
| tables get | 30 columns | 30 |
| query | 100 rows | server default with --page-size 0 |
schemas list pages server-side; schemas get and tables get page after
the fetch, because Incorta returns a whole schema in one response — the
window bounds what you read, not what crosses the network, and totalCount
always reports the full set. A --limit above the max is refused rather
than quietly trimmed: a truncated page is indistinguishable from a short
one. Page on with --offset (or "start_row" for query).
query reads a business view only, and every field — including columns
inside a formula — must come from one business schema and one view:
The query itself is one JSON document, not a wall of flags — the thing you can save, diff, and paste back:
# flat extract, CSV, straight into a file
appkit query '{"measures":["HR_BS.Employee_BS.SALARY"]}' --csv > pay.csv
# grouped, sorted, filtered
appkit query '{
"measures": [{"field": "HR_BS.Employee_BS.SALARY", "aggregation": "sum"}],
"rows": ["HR_BS.Employee_BS.DEPT"],
"aggregate": true
}' --json
# from a file, or piped in
appkit query --body query.json
appkit query - < query.jsonOnly measures is required. Without "aggregate": true you get a flat
extract; with it, Incorta groups by the row dimensions and folds each
measure. filters is the WHERE, aggregate_filters the HAVING. Both
spellings of every key work (page_size or pageSize), and a key the
document does not recognise is named and refused rather than silently
dropped — a typo that changes the answer is worse than one that stops it.
appkit query --help prints the whole shape.
The only flags left are about output, not the question: --csv, --json,
and --body.
These commands always need a token — appkit login or INCORTA_TOKEN.
There is no unauthenticated path, because there is no such thing as
"everyone's data".
Or just write SQL
query speaks Incorta's analytics api — business views,
measures, dimensions. When you would rather write SQL, sql runs
it against the same data through Osaka's SQL engine:
appkit sql 'SELECT * FROM "SALES"."COUNTRIES"'
appkit sql "SELECT country, SUM(revenue) AS total FROM \"SALES\".\"ORDERS\" GROUP BY country" --limit 20
appkit sql "SELECT table_name FROM information_schema.tables WHERE table_schema = 'SALES'" --jsonThis runs as you as literally as it gets: your bearer token is what
authenticates the SQL connection, so your Incorta grants and row security
(RSF, $user) are the only limits — the same statement, run by someone
else, sees their rows.
Three things worth knowing before your first query:
- Quote identifiers exactly as Incorta reports them. Unquoted names fold
to lowercase and will not match —
FROM "SALES"."COUNTRIES", notFROM SALES.COUNTRIES. Column names are lowercase. - Values go inline. There is no parameters argument, and the endpoint
rejects
$1-style placeholders outright. - Aggregate in SQL; there is no cursor. A row-returning query with no
LIMITof its own gets one (default 50, ceiling 200 —--limitabove that is clamped, not refused). When a cap is what ended the result rather than the data, the output says so, so(200 rows)is never mistaken for the real count.
Discover the catalog with SQL too — information_schema.schemata,
.tables, .columns all work — or with appkit schemas list, which
is cheaper for "what schemas exist at all".
If a deployment has no SQL endpoint configured, this command says so plainly instead of telling you to retry: it is a setting, not an outage.
Automate everything
Every interactive step has a headless twin, and --json is everywhere:
appkit create --name "Sales Dash" --stack tanstack --json
appkit push --commit -m "nightly sync"
appkit publish --accept-data-loss --accept-advisories --accept-code-findings --json
appkit secrets set --production API_KEY="$KEY" --json # restarts the live app
appkit secrets rm --production OLD_KEY --yes
appkit revert 3 --yes
appkit apps list --json --limit 50 --offset 50appkit apps list shows the 20 most recently updated apps and prints the
command for the next page. Narrow it with --search <text> and walk it with
--limit (up to 50) and --offset; --json emits just that page as an
array, so a script pages until one comes back short.
In CI, skip the browser entirely — a token authenticates every command:
INCORTA_TOKEN=<token> appkit publish --jsonCommand reference
| Command | What it does |
|---|---|
| appkit login [url] | Sign in to an environment with your browser |
| appkit logout | Remove the stored session |
| appkit whoami | Who you are signed in as, and where |
| appkit create | Create an app on the platform and clone it here (--agent, --skill, --scope — see Build apps with any coding agent) |
| appkit clone <app> | Clone an existing app (by id or name) — same --agent/--skill/--scope |
| appkit dev | Run the app locally, signed in, on real data |
| appkit publish | Build a version and put it live on its stable subdomain; --version <n> rolls back (built again), --subdomain <name> picks the address |
| appkit publish --confirm <runId> | Review and confirm a publish run parked for confirmation |
| appkit unpublish | Take the published app offline |
| appkit push | Push local commits — each push mints a version |
| appkit pull | Fast-forward your folder to the workspace |
| appkit status | Drift, versions, and agent activity at a glance |
| appkit versions | Version history with current and live marked |
| appkit revert <n> | Restore an older version (history is kept) and sync the development database to it |
| appkit logs | Read or --follow the app's output |
| appkit secrets | set / list / rm runtime secrets — development by default, --production for the published app's own set |
| appkit secrets apply | Apply saved production secrets — one restart of the published app (--restart retries a restart that did not go through) |
| appkit secrets add-to-production [names] | Copy development secrets into production — named ones, --all for every one it lacks, or pick from the list on a terminal |
| appkit env pull | Write the runtime env into .env for local runs |
| appkit db push | Apply db/schema.ts to the development database |
| appkit db types | Regenerate database bindings without changing the database |
| appkit open | Open the live app, or the builder (--preview / --platform) |
| appkit skills list | Skills installed here, or available to install (--installed) |
| appkit skills install | Install skill(s) into a coding agent (--skill, --agent, --scope) |
| appkit skills update | Update installed skills to their bundled version (--skill, --all) |
| appkit apps list | Your applications on the platform, 20 per page (--search, --limit, --offset) |
| appkit schemas list | Schemas you can see — --type all\|physical\|business |
| appkit schemas get <schema> | One schema's tables (physical) or views (business) |
| appkit tables get <schema> <table> | The columns of one table or view |
| appkit query '<json>' | Rows from a business view — table, --csv, or --json |
| appkit sql "<sql>" | Run SQL against your Incorta data through Osaka |
| appkit config | Inspect the environment the CLI talks to |
| appkit doctor | Check setup, connectivity, and login health |
Every command accepts --json where output matters, and appkit help
<command> shows the full flags. --json is headless even on a terminal:
no prompt ever writes into the document, so a secrets set NAME without a
value or a secrets rm without --yes fails naming the flag to pass
instead of asking.
appkit publish flags for the review flow:
| Flag | Effect |
|---|---|
| --accept-data-loss | Headless: acknowledge every destructive schema change in the review |
| --accept-advisories | Headless: acknowledge every dependency advisory in the review |
| --accept-code-findings | Headless: acknowledge every code-analysis finding in the review |
| --show-sql / --verbose | Print the exact SQL the run will execute (always present in --json) |
| --no-wait | Return once the run is confirmed instead of following it to the end |
| --confirm <runId> | Resume a parked run instead of starting a new one |
With --json, a publish that finished synchronously prints
{ appId, subdomain, url, version } as before; a publish that went through
a run prints { appId, run, acknowledgements, result? } — the run's
plan_schema step carries the full plan, result the live address.
appkit status --json carries the publish status verbatim under publish,
productionSecrets.pendingCount included.
appkit secrets with --json:
| Command | Document |
|---|---|
| secrets list | { appId, secrets } — the development rows |
| secrets list --production | { appId, secrets, pendingCount, developmentOnly } — each row { name, source, pending, appliedAt, updatedAt, createdAt } |
| secrets set | { appId, created, updated } |
| secrets set --production | { appId, created, updated, applied, restarted, pendingCount } — printed even when the apply is refused, so a script sees what is saved and pending |
| secrets rm [--production] | { appId, removed } |
| secrets apply | { appId, appliedCount, restarted } (0 / false when nothing was pending — no restart; --restart posts regardless and reports the api's restarted) |
| secrets add-to-production | { appId, added, applied, restarted, pendingCount } |
--production flags: set takes --no-apply (save without restarting);
rm needs --yes headless like the development one — and restarts the
live app on the spot, once per name; add-to-production takes --all
and --no-apply, and headless needs names or --all.
Upgrading from an earlier release
appkit deploy is retired. Previews are no longer stored builds with a URL
each: the builder builds any version on demand, and the only stored build
is the published one, produced by the publish run's build step. Where a
script ran appkit deploy, run appkit push (a version) and
appkit publish (built, checked, live); appkit deploy itself still
parses its old flags and fails with that guidance. appkit open --preview
opens the builder. appkit versions shows no build column any more, and
the version documents of --json carry no preview* fields.
The platform release that builds previews on demand needs appkit 1.2 or
later — an older binary's deploy is answered with an HTTP 410, and its
publish refuses every version as "never built". Upgrade with
npm install -g @incorta/appkit@latest.
Environment variables
| Variable | Effect |
|---|---|
| INCORTA_TOKEN | Bearer token for CI and scripts (overrides the login session) |
| APPKIT_API_URL | Point one invocation at a different api |
| INCORTA_CONFIG_DIR | Use a different config directory (default ~/.incorta) |
| APPKIT_BROWSER | Command used to open sign-in URLs (none prints them only) |
| APPKIT_DEBUG | Print stack traces on unexpected errors |
Something off?
appkit doctorOne command checks your runtime, git, environment, connectivity, and login — and says exactly what to fix.
