Deploy a model
A deployment is solver code plus data, deployed under a name. Once it exists, anyone in your
company can create a project from <company>/<name> and start solving.
The layout contract
Section titled “The layout contract”The server expects two trees. The solver reads ../data/ at solve time, so the same solver can run
against a different dataset.
Directoryrun/
- pyproject.toml the solver package’s own metadata
- run.py
Directorysrc/
Directorysolver/
- **/*.py
Directorydata/
- instances.json
- distances.csv
checkDeployment validates this server-side and reports what is missing.
Deploying
Section titled “Deploying”aura deploy reads a [tool.aura.deployment.<name>] table, normally in the model folder’s own
pyproject.toml — the file that lands as run/pyproject.toml. Build backends ignore unknown
tool tables, so the config travels with the artifact without affecting it.
[tool.aura.deployment.vrp]model_folder = "." # uploaded under run/ (default ".")data_folder = "../define/data" # uploaded under data/ (required)include = ["pyproject.toml", "run.py", "src/solver/**"]data_include = ["**"]exclude = ["notes/**", "personal_infos.txt"]aura init # scaffold the section aboveaura deploy --dry-run # print exactly what would upload, change nothingaura deploy # uploadaura deployment check # server-side run contractSeveral deployments may live in one pyproject.toml; aura deploy <name> picks one, and the
name is optional when only one is defined. aura deploy searches upward from the current
directory for a pyproject.toml carrying a [tool.aura] table, or takes --config <path>.
import { AuraClient, deployFromFolder } from "@strangeworks-inc/strangeworks-aura-sdk";
const aura = new AuraClient({ apiKey: process.env.AURA_API_KEY! });
await aura.createDeployment("vrp");
await deployFromFolder(aura, "vrp", "./my-model/solver", { destPrefix: "run/" });await deployFromFolder(aura, "vrp", "./my-model/data", { destPrefix: "data/" });
const check = await aura.checkDeployment("vrp");if (!check.ok) { throw new Error(`Deployment not runnable: ${check.missing.join(", ")}`);}import asyncio, osfrom aura_sdk import AuraClient, deploy_from_folder
async def deploy() -> None: async with AuraClient(api_key=os.environ["AURA_API_KEY"]) as aura: await aura.create_deployment("vrp")
await deploy_from_folder(aura, "vrp", "./my-model/solver", dest_prefix="run/") await deploy_from_folder(aura, "vrp", "./my-model/data", dest_prefix="data/")
check = await aura.check_deployment("vrp") if not check.ok: raise RuntimeError(f"Deployment not runnable: {', '.join(check.missing)}")
asyncio.run(deploy())Choosing what uploads
Section titled “Choosing what uploads”include and data_include are glob lists relative to their folder; exclude applies to both
and wins over include.
include = ["pyproject.toml", "run.py", "README.md", "src/solver/**"]data_include = ["*.csv", "instances/**"]exclude = ["**/scratch/**", "notes.md"]Built-in excludes always apply on top: dot-prefixed files and directories (.git, .venv,
.env, …), __pycache__, node_modules, dist, venv, *.pyc. The server additionally
rejects dot-prefixed and executable paths.
Preview what would upload:
aura deploy --dry-runfilter keeps only the paths you return true for; paths are POSIX-relative to the folder.
Nothing is excluded by default — unlike aura deploy — and the server refuses dot-prefixed
segments (.git, .venv, .DS_Store, .env) with a 400, so skip those too.
await deployFromFolder(aura, "vrp", "./my-model/solver", { destPrefix: "run/", filter: (path) => !path.includes("/scratch/"), concurrency: 8, onFileUploaded: (path) => console.log(`uploaded ${path}`),});include is a predicate over the POSIX-relative path. Nothing is excluded by default — unlike
aura deploy — and the server refuses dot-prefixed segments (.git, .venv, .DS_Store,
.env) with a 400, so skip those and anything else you would not ship:
def shippable(path: str) -> bool: parts = path.split("/") return not any(part.startswith(".") or part == "__pycache__" for part in parts)
await deploy_from_folder( aura, "vrp", "./my-model/solver", dest_prefix="run/", include=lambda path: shippable(path) and "/scratch/" not in path, concurrency=8, on_file_uploaded=lambda path: print(f"uploaded {path}"),)Shipping the agent’s instructions with it
Section titled “Shipping the agent’s instructions with it”Two optional config keys travel with the same aura deploy, describing how the agent should
operate the model rather than what it is:
[tool.aura.deployment.vrp]data_folder = "../define/data"agents_md = "../agent-config/AGENTS.md"skills_folder = "../agent-config/skills"Keep both outside model_folder — with the default model_folder = "." and include = ["**"],
anything beside the solver also uploads under run/. Instructions and
skills covers what to write in them.
Redeploying is a sync
Section titled “Redeploying is a sync”Remote run/ and data/ are made to match your local folders: after the uploads, remote files no
longer present locally are removed, so renames and deletions leave nothing stale. If an upload
fails, the sync stops without pruning.
That is aura deploy. The SDK helpers, deployFromFolder and deploy_from_folder, upload and
nothing else — a file renamed or deleted locally keeps its old copy on the server. To sync from the
SDK, diff listFiles / list_files against your folder and delete what is missing locally, or
deploy with the CLI.
Agent config syncs only where you declared it: omitting agents_md or skills_folder leaves
whatever is already uploaded alone rather than deleting it, while declaring a key makes the local
tree authoritative and prunes remote entries missing locally.
Create a project from it
Section titled “Create a project from it”-
Check it is runnable:
Terminal window aura deployment check vrp -
Create a project from the handle,
<company>/<name>:Terminal window aura project create acme/vrp --name "vrp demo" -
Chat with it, or give the agent instructions and skills for operating it first.