Repository navigation
AutoAir 0.2.0
Added
- Kustomize manifests for Kubernetes (
deploy/kustomize): a hardened base forautoair serve
(non-root, read-only root file system, no service account token, probes on/api/healthand
/api/ready, PodDisruptionBudget, topology spread), Components for PostgreSQL state with an
autoair db upgradeinit container, the git deploy target, the validator service (no egress),
the AI service, a separate MCP server, a Gateway API HTTPRoute (the production default) or an
Ingress, HorizontalPodAutoscaler,
NetworkPolicies and Prometheus ServiceMonitors, anddev,productionandairflow-plugin
overlays. Secrets are referenced, never included; credentials stay Airflow connections. CI
builds every overlay and validates it with kubeconform. New page: Deployment → Kubernetes
with Kustomize. - AutoAir docs MCP: AutoAir's own documentation (the website pages and the configuration
reference) is packaged in the wheel at build time (hatch_build.py,autoair.docs.build) and
served to coding agents by the MCP toolssearch_autoair_docs,read_autoair_docand
list_autoair_docs: offline, matched to the installed version, without Airflow, the AI extra
or a knowledge-base store.autoair docs-mcp(stdio, or--http) serves only these tools,
e.g.uvx --from 'autoair[mcp]' autoair docs-mcp; they are also on every AutoAir MCP endpoint
(no AutoAir permission, feature flagfeatures.autoair_docs). Setup for Claude Code, Cursor,
OpenCode and Codex CLI: website page AI → Docs MCP. - Knowledge base: built-in source kind and component
autoair(AutoAir's packaged
documentation, no network), sokb_searchand the assistants cite AutoAir pages. - Isolated import checks:
autoair validator-serveruns the DagBag import check in a separate
validator service, so the DAG code it imports (raw-code DAGs, Python task bodies) no longer
executes in the Airflow API server orautoair serve(Python DAG import and test runs still
do). Setvalidator.url
(AUTOAIR_VALIDATOR__URL); requests are HMAC-signed with the password of the Airflow
connectionvalidator.secret_conn_id(no new secret setting). Workers get a fresh interpreter
per check, memory/CPU/file-size/open-file limits, no new privileges, a network namespace where
the kernel allows it and a Python audit hook (no subprocesses, sockets, DNS, writes outside
their scratch folder or reads of other processes'/proc). Retries, a health probe and
validator.fail_mode(openby default: animport_servicewarning and no import check
while the validator is down;closedblocks deploys instead;local)./health,/ready,/metrics
(autoair_validator_*) on the validator,autoair_import_checks_totalon the main server.
New Helm chartdeploy/helm/autoair-validator(Deployment, Service, PodDisruptionBudget,
NetworkPolicy denying all egress, read-only non-root pods) and Compose overlay
deploy/compose/docker-compose.validator.yml. New page: Deployment → Validator service. - Astronomer Blueprint steps: templates of
airflow-blueprint(installed packages with the
airflow_blueprint.blueprintsentry point,blueprints.template_dirs,blueprints.modules)
appear in the palette's Blueprints (Astronomer) section and becomekind: blueprintnodes
with a config form generated from the template's Pydantic model, version selection, a task
preview (inspector and collapsible node) and validation against the config schema. Python and
loader output callTemplate().render(Config(...)); the YAML output mode reports them as
unsupported, the dag-factory export skips them. The Python importer foldsrender()calls
back into blueprint steps. New extraautoair[blueprint], feature switch
features.blueprints, settings sectionblueprints, routes/api/blueprints/*and the MCP
toollist_blueprints. Guide: User guide → Blueprint steps. - Airflow 3.4:
plugin.dag_tab_tags(AUTOAIR_PLUGIN__DAG_TAB_TAGS) shows the AutoAir
DAG tab only on DAGs with one of the listed tags, through the plugin'sapplies_to
scoping. Empty (default) keeps the tab on every DAG page; ignored before Airflow 3.4. - Validation warns about task groups that depend on each other in a cycle
(task_group_cycle), which Airflow 3.4 deprecates and Airflow 3.5 is planned to reject. - Editing existing Python DAGs keeps the hand-written file: each DAG has a source policy
(auto,generate,preserve,strict-preserve; DAG settings and the deploy dialog).
Imported and code-mode DAGs default to preserve: a deploy applies only the change to the
existing file with a LibCST patch (keyword arguments, TaskFlow bodies, added and removed
tasks and dependencies, renames), verifies it with a round-trip import and the DagBag
check, and never regenerates the file on its own. The deploy dialog previews the DAG file
as a unified diff (POST /api/specs/{dag_id}/source-preview, MCPpreview_dag_source);
refused changes explain why and offer raw code or a deliberate switch togenerate.
A stopped deploy says why by the file's state (the hand-written file AutoAir keeps, a file
changed outside AutoAir, or one AutoAir did not create); "Keep as raw code DAG" and
"Overwrite the file" follow the user'scode:rawanddeploy:overwrite_ejected; a source
policy picked in the deploy dialog changes the DAG only when it deploys (Cancel keeps it).
Code mode and the Code tab show the hand-written file. New settingspreserve.*, feature flags
source_preserveandstrict_source_preserve.libcstis a new core dependency. - Generated DAG files carry a fingerprint line (
# autoair:fingerprint sha256=…), so a file
edited outside AutoAir is recognised on folder, object-storage and git targets even without
AutoAir's state. Such a deploy stops and offers to apply the change to the edited file
(merged with the outside edits);preserve.ejected_files(block,warn,overwrite)
sets the behaviour. New permissiondeploy:overwrite_ejected(Admin; never from a
wildcard) for a confirmed overwrite. - Tool-call approval for AI agents (pydantic-ai deferred tools): when the developer or admin
assistant or a troubleshooting analysis calls a tool that changes something (remember,
external MCP tools not markedreadOnlyHint), the run pauses with an approval card showing
the tool and its masked arguments; the user who started the run approves or denies each
call (optionally with a reason) and the run resumes in the same answer or analysis.
Approvals are stored with the state (multi-replica safe, decided at most once, expiring
afterai.approvals.ttl_seconds) and audited. New settingsai.approvals.*, new endpoints
GET /api/ai/approvals,GET /api/ai/approvals/{id}and
POST /api/ai/approvals/{id}/stream. New page: AI → Tool-call approval. - Large DAGs: above 150 nodes the canvas can zoom out to an overview of the whole graph
(Fit view), and below 40 % zoom task cards turn into light, colour-tinted boxes with
their name (problems, run states, diff and selection stay visible). Benchmarks for large
DAGs:scripts/gen_large_dag.py(synthetic specs),scripts/bench_large_dag.py(server
timings) andfrontend/e2e/large-graph.spec.ts(load, layout, frame rate, memory). New
page: User guide → Large DAGs (measured limits and tuning). preserve.allow_force_overwrite(AUTOAIR_PRESERVE__ALLOW_FORCE_OVERWRITE, default
false): the "Overwrite (force)" choice is offered and accepted only when it istrue, for
the hand-written file AutoAir keeps for a DAG, a DAG file changed outside AutoAir andforce
deploys of the loader and YAML output modes and raw code. The caller still needs
deploy:overwrite_ejectedand an explicit confirmation;/api/metareports
allow_force_overwrite, and the source-plan and conflict dialogs hide the overwrite button
otherwise. Source plans refuse with the new codeoverwrite_disabled.server.harden_process(AUTOAIR_SERVER__HARDEN_PROCESS, defaulttrue) controls whether
autoair serve,autoair local(and their uvicorn workers),autoair mcp-serve --httpand
autoair ai-servemake their process non-dumpable. Turn it off only to debug with py-spy,
gdb or core dumps; the server then logs a warning at start-up. Never applied in the Airflow
plugin.
Changed
spec_versionis now 3. Version 3 adds only optional parts (sourceand theblueprint
node kind); version 1 and 2 documents keep loading and are written as version 3 the next
time they are saved or deployed. Upgrade note: AutoAir 0.1.0 refuses version 3 specs
with "spec_version 3 is newer than this AutoAir supports (2); upgrade AutoAir", also in the
loader stubs inside Airflow. Upgrade every AutoAir process that reads the same specs
together (plugin,autoair servereplicas, MCP and AI services, theautoairpackage in
Airflow's dag-processor and workers); rolling back to 0.1.0 needs the specs from before the
upgrade.- Overwriting a DAG file AutoAir would otherwise not replace now needs
preserve.allow_force_overwrite=true(defaultfalse).preserve.ejected_files=overwrite
takes effect only with it (otherwise it behaves likeblock, also for administrators), and
regenerating the kept hand-written file (switch to generate) needs it as well as
deploy:overwrite_ejected. Deleting a kept hand-written DAG still needs only the
permission. Files without AutoAir's marker are never overwritten byforce. A deploy that
would replace the kept hand-written file without a source plan (another output mode, a
conversion to raw code) returns HTTP 200 withdeployed: false,status: "ejected"and
conflict.ejected_file,conflict.hand_writtenandconflict.force_allowedinstead of
HTTP 409; ejected results without a source plan also carryconflict.force_allowed.
Upgrade note: deployments that relied onpreserve.ejected_files=overwriteor on
regenerating kept hand-written files must setAUTOAIR_PRESERVE__ALLOW_FORCE_OVERWRITE=true;
API clients that handled the 409 must readstatus: "ejected"instead. - MCP
save_raw_dag_code: a raw code DAG that replaces its own code no longer needs
deploy:overwrite_ejected, like the REST deploy (code:rawis still required over HTTP).
The file is written only while it is unchanged since AutoAir wrote it; MCP has noforce,
so a file changed outside AutoAir is refused. Replacing a visual DAG's kept file is still
refused. - Upgrade note: the validator service (
autoair validator-serve) and the main server
must run the same AutoAir version; there is no mixed-version compatibility window. Upgrade
and roll back both together. - Knowledge base defaults:
kb.sourcesis nowofficialplusautoair, andkb.components
includesautoair. Withkb.refresh_on_startthe server now builds the (small) index of
AutoAir's own documentation at start-up even when air-gapped. Akb.sourceslist you set
replaces the default; add{"id": "autoair", "kind": "autoair"}to keep AutoAir's pages. - The warm DagBag pool exchanges JSON with its workers instead of pickles, so DAG code that
writes to the worker's pipe can no longer execute code in the AutoAir process when the
parent reads the reply. Workers run in their own process group, and stopping one also stops
the processes it started. - CI tests the Airflow 3.4.0b2 pre-release (was 3.4.0b1). No final 3.4 exists yet. Airflow
3.4 needs Python 3.11 or newer; AutoAir keeps supporting Python 3.10 with Airflow 3.0-3.3. - A local deploy whose reparse request Airflow refuses (
403, for example a file without a
registered DAG on Airflow 3.4 without theREPARSE_ALLpermission, or404) says so in the
deploy message instead of "could not contact Airflow"; the deploy itself succeeds. A
409(a reparse of the file is still queued) is reported as "reparse already requested". - The deploy option
forceno longer overwrites a Python DAG file that was changed outside
AutoAir: answer the stopped deploy withsource_resolutioninstead. For loader stubs it
needsdeploy:overwrite_ejected. - Deployer cannot deploy a DAG that keeps its hand-written Python file: the round
trip needscode:executeand writing the imported file needscode:raw. The refusal
names the missing permission. New built-in role CodeDeployer (Deployer +
code:execute+code:raw) for those deploys; or deploy with thegeneratepolicy.
Regenerating a hand-written file AutoAir keeps needsdeploy:overwrite_ejectedand
preserve.allow_force_overwrite=true; deleting it needs the permission. - Imported DAGs (Python file, Airflow, repository import, MCP) and code applied in the code
mode keep their Python file (source.origin: python); deploying it as written needs
code:executefor the round trip andcode:rawwhen the file comes from the spec. The
repository import dialog can generate the files instead. Promoting such a DAG between
environments is refused (409). - Calls of
rememberand of external MCP tools withoutreadOnlyHint: truenow wait for
the user's approval by default (previously they ran directly). Exempt tools with
ai.approvals.default.exemptorai.approvals.agents.<agent>.exempt. Troubleshooting
analyses can have the new statusawaiting_approval. - Validation lints all Python task bodies with one ruff run and shell scripts with one
ShellCheck run per 50 scripts, and remembers results of unchanged bodies (a failed run is
retried, not remembered;.shellcheckrcfiles are no longer read): validating a 1,000-task DAG takes
about 0.3 s the first time and 0.1 s afterwards (was 2-5 s, one process per task).
editor.python_lint_timeoutandeditor.shellcheck_timeoutnow apply per batched run. - Editor performance on large DAGs: dragging no longer serialises the whole document for undo
history, live checks and collaboration on every frame; node badges, run-state overlays and
the inspector no longer re-render or rescan every node per change; live checks wait longer
after the last change on DAGs with more than 200 nodes (up to 2 s); an auto layout that
finishes after the graph changed is discarded instead of applied. - Testing: visual regression baselines inside the Airflow UI (
npm run test:visual:plugin):
the eight standalone screens plus the connection form, clear and backfill dialogs and asset
actions, light and dark, against a private Airflow with the plugin. The browser runs in the
Playwright Docker image so the baselines match on every Linux machine and in CI (the
non-blocking Frontend · visual regression job runs both suites). autoair[all]no longer includesmcp: FastMCP 4 needs Starlette 1.x, which Airflow 3.1's
FastAPI pin excludes, soallnow installs next to every supported Airflow. Use
autoair[all,mcp]on Airflow 3.0, 3.2 and newer or standalone. Thesqlite,postgresand
kb-pgvectorextras accept SQLAlchemy 1.4.49 or newer. New page: Installation →
Compatibility (extras per Airflow version).
Fixed
- Airflow 3.4: the latest log of a task waiting for a retry (
up_for_retry) is the failed
try again, not the upcoming try that has no log yet (run panel, AI troubleshooting and fix,
MCPget_task_log).GET /api/airflow/capabilitiesreportsretry_try_is_next. - The SQLite and PostgreSQL state backends and the pgvector knowledge-base store work on
SQLAlchemy 1.4, the version Airflow 3.0 installs (previously anImportErroron
sqlalchemy.Connection). On SQLAlchemy 1.4 PostgreSQL goes through psycopg2. - Without
autoair[mcp]the MCP feature is reported unavailable with the reason
(/api/metamcp_unavailable_reason, admin Features tab) instead of only being unmounted. - Python 3.10: git history timestamps ending in
Zand$type: datetimeparameters sent by
the browser (toISOString) no longer fail to parse. - Python DAG import on Airflow 3.2:
CronTriggerTimetable("@daily", ...)is recognised as a
preset again (SDK timetables keep the preset unresolved). - Security: a hand-written DAG file adopted over MCP (
save_dag_specwith source policy
preserve) is recorded as kept, like a REST deploy, so regenerating or deleting it needs
deploy:overwrite_ejected. The deploy targets (local folder, object storage, git) now refuse
to replace or remove a kept file on every write path that skips the source plan: switching
the DAG to the loader or YAML output mode, converting it to a raw code DAG, MCP saves and
promotions. A deploy withforceby a holder ofdeploy:overwrite_ejectedstill may, with
preserve.allow_force_overwrite=trueand only for a file with AutoAir's marker; a raw code
DAG keeps editing its own code. - Security: a DAG that writes part of a reply to the import-check worker's pipe and stops no
longer hangs a DagBag pool thread (and, in the validator service, a capacity slot until a
restart): the check's deadline covers reading the whole reply, after which the worker is
replaced. Replies carry a per-check nonce, so code left running from an earlier check
cannot answer for a later one. - Security: the validator service signs its
/v1/checkanswers with the HMAC key, bound to
the request's nonce (x-autoair-validator-response-signature); the main server rejects
unsigned or mismatched answers and treats the validator as unavailable
(validator.fail_modeapplies). Update the validator and the main server together. - Security:
autoair serve,autoair local,autoair mcp-serve --httpandautoair ai-serve
make their process non-dumpable at start-up (Linux), so DAG code in their child processes
cannot read the server's environment through/proc. The Airflow plugin leaves Airflow's
process unchanged; usevalidator.urlthere. - Security: denial reasons of AI tool approvals are scrubbed of secrets and passed through the
guardrails before they reach the model, the stored steps and the audit record. - Security: for a hand-written file AutoAir did not write (content hidden), deploy results no
longer return the mergedstored_spec, and import and round-trip errors no longer quote the
file. - Security: the MCP
save_dag_specvalidates a preserved hand-written file with the same
inputs as the REST deploy (operator templates, target Airflow version, user and action for
OPA). - Blueprint task previews report an unavailable validator service as a preview error instead
of HTTP 500. - The deploy audit record and commit context say when the import check was skipped
(validation.import_check_skipped:fail_openwhen the validator was unavailable,
permissionwithoutcode:execute). - The validator's Kustomize NetworkPolicy admits Airflow api-server pods only from the
namespaceairflow(a placeholder to adjust) instead of from every namespace.
Install: pip install autoair==0.2.0 ·
Image: ghcr.io/ctolon/autoair:0.2.0