Skip to main content
Version: 1.0.0

Touba Project and prepare

Select a handler process​

A host decision can provide a binding's complete process: kind, command, args and, when needed, cwd.

host-decisions.json
{
"bindings": {
"tool:orders.review/ReadOrder": {
"handler": {
"kind": "process",
"command": ".venv/bin/python",
"args": ["-B", "handlers/read_order.py"],
"cwd": "."
}
}
}
}
touba prepare --mode plan --host-decisions host-decisions.json
touba prepare --mode write --host-decisions host-decisions.json

On Windows, use .venv/Scripts/python.exe. The process is replaced as a whole: previous arguments are not retained. Choose either handler or package_handler for a binding. Edit fragment-managed bindings in their fragment. Grants, secrets and the effect contract remain separate explicit decisions.

This guide explains how to organize a small Touba project without manually managing every derived artifact.

The commands in this guide assume that the Touba CLI is available in the terminal. Installing only the VS Code extension through VSIX does not provide the touba command.

Role​

A Touba project connects these elements:

ElementRole
.tb filedeveloper-readable source
modulelogical identity declared in the file
project configurationentry, profiles, and explicit authority-configuration references
build projectionAIR, assembled manifest, and run plan derived for one profile
manifesteffective tool execution contract derived from declared authority
handlerhost code called by a tool
ownershipteam responsibility, owners, and review gates

A .tb file does not always need a manifest or handler. A project needs them when prepared AIR contains a tool to execute, whether that tool is declared in the current file or imported from another module in the same project.

Explicit host decisions​

Pass decisions to prepare instead of writing a manifest-editing script. For example, for ReadOrder in the orders.review module:

Terminal
touba prepare --mode plan --host-decisions-json '{"bindings":{"tool:orders.review/ReadOrder":{"grants":["network"],"effect_contract":{"kind":"none"},"timeout_ms":5000}}}'

The report shows the diff and source requirements. After reviewing it, repeat with --mode write, then --mode check. Only write modifies files. Adapt the reference and decisions to your project; kind: "none" is only suitable for a handler with no external writes.

Allowed fields: grants, secrets (handles, never values), memories, effect_contract, timeout_ms, max_payload_bytes, sandbox. The top-level agent_secret_scopes object selects authorized handles per agent. Selections must match source-declared requirements. Omitted fields are preserved; an empty list revokes that selection. Missing permissions remain visible and are never filled automatically.

Repeated decisions are idempotent. Invalid input prevents all preparation writes. Modify fragment-owned bindings in their source fragment instead; any drift against the current manifest is refused until explicitly reconciled. The manifest remains runtime authority, with no additional registry. Existing locks must be checked and explicitly renewed after an authority change. Never pass secret values on the command line.

Module Convention​

Keys in touba.project.json and its configuration object are closed: unknown keys fail with ProjectConfigFieldUnknown. Put contexts inside configuration.contexts, never in a root-level contexts field.

Inside a project, the convention is:

Structure
src/examples/search.tb

with:

source.tb
module examples.search

The folder part gives the logical domain. The filename gives the last module segment.

Initialize a Project​

Syntax:

Terminal
touba init

Effect:

  • creates expected project directories;
  • creates touba.project.json with JavaScript, @touba/sdk, the touba-artifacts-v1 layout, and pure / dev / prod profiles;
  • separates derived outputs under .touba/build/, state under .touba/state/, and local evidence under .touba/evidence/;
  • prepares project exclusions;
  • creates no manifest until a tool exists.

For Python handlers, use touba init --language python, then touba sdk install python. For JavaScript, use touba sdk install javascript. Both SDKs use the same concepts; only the host language changes.

Explicit host authority​

prepare exposes source requirements in manifest_requirements. Generated manifests grant no permissions, secrets or memory scopes; triggers are disabled. Review requirements, implement handlers and configure host permissions explicitly. needs_changes means work remains, not that execution is ready.

Pass --manifest touba.manifest.json to keep the reviewed manifest outside the build, or configure its path in the project. Preparation preserves existing host decisions and business handlers. New requirements never silently expand authority.

Only touba-artifacts-v1 is accepted: .touba/build/<profile>/ for builds, .touba/state/ for state. The single strategy, or otherwise single executable entry, is selected when unambiguous. Otherwise use --entry <ref>. No historical layout fallback or implicit migration is performed.

Default project entry​

With several strategies, add the top-level property "entry": "strategy:app.support/Run" to touba.project.json, using an actual entry reference and preserving other fields. Both write and check preparations then share this choice.

For a one-off selection, pass the same --entry <ref> to both commands. A write with that option followed by check without it does not check the same effective project: EffectiveProjectDrift or select_entry is expected. No strategy is selected arbitrarily.

Create a Module​

Syntax:

Terminal
touba new module examples.hello

Effect:

Structure
src/examples/hello.tb

with:

source.tb
module examples.hello

A project module name must contain at least two lowercase segments, for example examples.hello.

Create a contextual package​

touba package new acme.review --context --yes creates the author manifest, procedure and README. Use touba package check review, then touba package add ./review to register it. See packages and context activation.

Prepare a Project Without a Tool​

Example:

source.tb
module examples.hello

strategy RunHello
gives Text
do
show
with message as "Touba ready"
give "done"

Command:

Terminal
touba prepare --mode plan

Expected result:

  • the report indicates which AIR would be produced;
  • no manifest is requested;
  • no handler is generated.

To write derived artifacts:

Terminal
touba prepare --mode write --profile pure

The project receives derived AIR under .touba/build/pure/air/. No manifest is produced because the program declares no tool.

Prepare a Project with a Tool​

Example:

source.tb
module examples.search

type SearchRequest
query is Text

type SearchResult
title is Text

tool SearchDatasets
takes request as SearchRequest
gives SearchResult
requires network

Command:

Terminal
touba prepare --mode write --profile dev

Expected result:

  • AIR is produced;
  • touba.project.json fixes the host language, SDK, and run profiles;
  • .touba/build/dev/touba.manifest.json receives the effective binding for SearchDatasets;
  • .touba/build/dev/touba.run-plan.json composes the entry and derived artifacts when entry is declared;
  • .touba/build/dev/touba.effective-project.json explains the resolved contract and each section’s provenance;
  • a handler stub is created under handlers/examples/search/.

The handler remains business code to complete. The stub is not a functional implementation.

The manifest contains no secret value. Review it as the project execution contract.

Compose a Binding Fragment​

A binding fragment describes an explicit wiring intent. It can come from a module, a local package, or a generation tool, but it is not a runtime artifact.

bindings/search.json
{
"fragment_schema_version": "touba-binding-fragment-v1",
"source": {
"kind": "module",
"module": "examples.search"
},
"bindings": {
"tool:examples.search/SearchDatasets": {
"handler": {
"kind": "process",
"command": "/absolute/path/to/node",
"args": ["handlers/examples/search/search_datasets.handler.mjs"],
"cwd": "."
},
"grants": ["network"],
"secrets": [],
"timeout_ms": 10000,
"max_payload_bytes": 1048576,
"payload_constraints": {
"input": {
"request.query": {
"non_empty": true,
"max_length": 80
}
}
}
}
}
}

Composition:

Terminal
touba prepare --binding-fragment bindings/search.json --mode write

prepare verifies that the tool exists in assembled AIR, that required grants and secrets are present, and that payload constraints are valid. It then writes the profile’s assembled manifest. touba run receives that derived manifest through an explicit run plan, never the fragment.

Validate Resource Bindings​

A resource binding connects a logical Touba resource to explicit host wiring. For resource bindings, Touba validates the contract while the backend remains host-provided. With resource binding transmission (RB-2), touba run --resource-bindings sends to the handler only the bindings associated with resources actually used by the called tool.

touba.resources.json
{
"resource_bindings_schema_version": "touba-resource-bindings-v1",
"resources": {
"memory:examples.memory/Notes": {
"kind": "memory_backend",
"provider": "host.project_memory",
"config": {
"endpoint_url": "https://memory.example.internal/notes"
},
"grants": ["memory.read"],
"secrets": ["DATABASE_URL"],
"required_secrets": ["DATABASE_URL"],
"limits": {
"max_payload_bytes": 1048576
}
}
}
}

Project validation:

Terminal
touba prepare \
--resource-bindings touba.resources.json \
--require-resource-bindings \
--mode check

Runtime validation:

Terminal
touba run --project . \
--entry strategy:examples.memory/Run \
--profile dev

Touba refuses an unknown resource, unsupported family, inactive binding, missing required grant or secret, missing memory scope, or a field that contains a raw payload or secret value.

In the handler protocol, the transmitted binding looks like this:

Handler request excerpt
{
"resource_bindings": [
{
"resource_ref": "memory:examples.memory/Notes",
"kind": "memory_backend",
"provider": "host.project_memory",
"grants": ["memory.read"],
"secret_handles": ["DATABASE_URL"],
"config": {
"endpoint_url": "https://memory.example.internal/notes"
},
"limits": {
"max_payload_bytes": 1048576
}
}
]
}

The handler uses this information as host wiring. It does not decide whether the resource is authorized: AIR, manifest, scope, grant, secret, and resource-binding checks have already been performed by Touba before dispatch.

Prepare a Project with Pure Imports​

The pure import slice (IM-1) allows explicit imports of type, non-sensitive define, and pure function.

Shared module:

src/examples/common.tb
module examples.common

type SearchRequest
query is Text
rows is Int

define DefaultRows as Int = 5

function NormalizeQuery
takes query as Text
gives Text
do
give query

Importing module:

src/examples/search.tb
module examples.search

import type SearchRequest from examples.common
import define DefaultRows from examples.common
import function NormalizeQuery from examples.common

strategy RunSearch
gives Text
do
show
with message as "search project ready"
give "done"

Command:

Terminal
touba prepare --mode write

Expected result:

  • project modules are compiled explicitly;
  • the prepared AIR for examples.search contains resolved pure imports;
  • no manifest or handler is created if no tool is declared;
  • Touba surfaces can inspect imports from prepared AIR.

Prepare a Project with Local Effectful Imports​

Touba allows local imports of tool, skill, strategy, agent, and memory declared in another module of the same project. The import makes the declaration available for composition, but does not move execution authority.

Declaring module:

src/examples/data_tools.tb
module examples.data_tools

type DatasetSearchRequest
query is Text
rows is Int

type DatasetSearchResult
title is Text
url is Text

tool SearchDatasets
takes request as DatasetSearchRequest
gives DatasetSearchResult
requires network

Importing module:

src/examples/research_app.tb
module examples.research_app

import type DatasetSearchRequest from examples.data_tools
import type DatasetSearchResult from examples.data_tools
import tool SearchDatasets from examples.data_tools

skill BuildDatasetBrief
takes request as DatasetSearchRequest
gives DatasetSearchResult
uses SearchDatasets
do
SearchDatasets
with request as request
into result
give result

Command:

Terminal
touba prepare --mode write

Expected result:

  • the prepared AIR for examples.research_app contains the resolved tool import;
  • the tool keeps its canonical reference tool:examples.data_tools/SearchDatasets;
  • touba.manifest.json receives or verifies the binding for this canonical reference;
  • the handler stub is created under handlers/examples/data_tools/, not under the importing module;
  • grants, secrets, limits, payload constraints, and supply-chain lock remain explicit.

Other effectful imports follow the same rule:

  • an imported skill or strategy keeps its graph and dependencies in prepared AIR;
  • an imported agent remains closed until its callable_by explicitly names the caller, including inter-module forms;
  • an imported memory remains a logical resource; an owner must declare the required read or write scope before calling an access tool.

Prepare a Project with a Local Package​

A local Touba package makes declarations available across projects. It grants no execution authority.

Possible structure:

Structure
my-project/
src/examples/package_app.tb
authors/research/
touba.package.json
src/org/example/research.tb

Package manifest:

authors/research/touba.package.json
{
"package_schema_version": "touba-package-v1",
"name": "org.example.research",
"version": "0.1.0",
"root": ".",
"touba": {
"air_profile": "touba-air-json-canonical-v1",
"runtime_compatibility": ">=1.0 <2.0"
},
"exports": {
"types": [
"type:org.example.research/DatasetSearchRequest",
"type:org.example.research/DatasetSearchResult"
],
"defines": [],
"functions": [],
"tools": [
"tool:org.example.research/SearchDatasets"
],
"skills": [],
"strategies": [],
"agents": [],
"memories": []
},
"provenance": {
"kind": "local_path",
"source": "."
}
}

Project configuration:

touba.project.json
{
"project_version": "touba-project-v1",
"language": "javascript",
"sdk": "@touba/sdk",
"packages": [
{
"manifest": "authors/research/touba.package.json",
"editable": true,
"version": "0.1.0"
}
],
"run_profiles": {
"dev": {
"require_supply_chain_lock": false
},
"prod": {
"require_supply_chain_lock": true,
"supply_chain_lock": "touba.lock.json"
}
}
}

Module supplied by the package:

authors/research/src/org/example/research.tb
module org.example.research

type DatasetSearchRequest
query is Text
rows is Int

type DatasetSearchResult
title is Text
url is Text

tool SearchDatasets
takes request as DatasetSearchRequest
gives DatasetSearchResult
requires network

Project module:

src/examples/package_app.tb
module examples.package_app

import type DatasetSearchRequest from org.example.research
import type DatasetSearchResult from org.example.research
import tool SearchDatasets from org.example.research

skill BuildDatasetBrief
takes request as DatasetSearchRequest
gives DatasetSearchResult
uses SearchDatasets
do
SearchDatasets
with request as request
into result
give result

Command:

Terminal
touba prepare --mode write

Expected result:

  • the package is loaded because it is declared explicitly;
  • only refs listed in exports are importable;
  • the prepared project AIR contains the useful imported declarations;
  • if an exported tool is actually imported, prepare produces the expected binding in touba.manifest.json and the stub under the declaring module;
  • the package cannot grant a grant, secret, handler, memory scope, agent caller, or active trigger.

Equivalent option without modifying touba.project.json:

Terminal
touba prepare --package authors/research/touba.package.json --mode write

Prepare a Run Plan​

A run plan groups project execution paths into an explicit artifact. It points to prepared AIR, the manifest if tools exist, the secrets profile if secrets are required, and the lock if the run profile requires it.

For a simple project:

Terminal
touba prepare \
--run-plan touba.run-plan.json \
--entry skill:examples.package_app/BuildDatasetBrief \
--mode write

Produced shape:

touba.run-plan.json
{
"run_plan_schema_version": "touba-run-plan-v1",
"entry_ref": "skill:examples.package_app/BuildDatasetBrief",
"air": ".touba/air/examples/package_app.air.json",
"manifest": "touba.manifest.json",
"secrets_profile": "touba.secrets.dev.json",
"profile": "dev",
"outputs": {
"mode": "show"
}
}

The run plan contains no business inputs, raw prompts, or secret values. Execution inputs are still supplied at run time.

For a strict profile:

Terminal
touba prepare \
--run-plan touba.run-plan.json \
--entry skill:examples.package_app/BuildDatasetBrief \
--profile prod \
--mode write

In this case, if the prod profile requires a supply-chain lock, the plan also points to touba.lock.json.

Validate Multi-Team Ownership​

A touba.ownership.json artifact describes responsible teams, owners, review gates, and reviews supplied for a profile.

touba.ownership.json
{
"ownership_schema_version": "touba-ownership-v1",
"teams": {
"team:platform": {
"display_name": "Platform"
},
"team:security": {
"display_name": "Security"
}
},
"owners": {
"tool:examples.search/SearchDatasets": ["team:platform"]
},
"review_gates": {
"prod": {
"requires_review_for": ["tool_binding", "required_capability"],
"reviewers": ["team:security"]
}
},
"reviews": [
{
"review_id": "review:search/security-001",
"profile": "prod",
"target_ref": "tool:examples.search/SearchDatasets",
"reviewer": "team:security",
"decision": "approved"
}
]
}

Validation:

Terminal
touba prepare \
--ownership touba.ownership.json \
--profile prod \
--mode check

With --require-ownership, Touba refuses if the ownership file is absent.

touba.ownership.json grants no runtime permission. It creates no handler, grant, secret, memory scope, agent caller, provider, lock, or manifest. It verifies that expected responsibility and reviews are present before moving toward a sensitive profile.

Check Coherence​

Syntax:

Terminal
touba prepare --mode check

This command fails if:

  • the expected manifest is missing;
  • a tool binding is missing;
  • an expected handler is missing;
  • an expected grant, secret, or memory access is not granted;
  • the expected run plan is missing, drifted, or contains a sensitive field;
  • an expected review gate is missing for the requested profile;
  • the ownership artifact attempts to introduce runtime authority;
  • the source file path does not match the module name.

Run Afterwards​

prepare does not launch the runtime.

To execute a program with a tool, the manifest remains explicit:

Terminal
touba run program.air.json \
--manifest touba.manifest.json

If a run plan was prepared:

Terminal
touba run --run-plan touba.run-plan.json \
--inputs-json '{"request":{"query":"transport"}}'

The runtime refuses before execution if the plan points to missing AIR, unknown entry, missing required manifest or secrets profile, or an incoherent strict supply-chain lock.

For a prod profile run, the project also forces the supply-chain lock declared in touba.project.json:

Terminal
touba run program.air.json \
--profile prod \
--manifest touba.manifest.json

For a program without a tool, no manifest is necessary:

Terminal
touba run program.air.json

Project Contract​

prepare coordinates the project-level contract:

  • pure imports (IM-1): type, non-sensitive define, and pure function;
  • effectful imports (IM-2): tool, skill, strategy, agent, and memory, without implicit authority;
  • explicit packages (PKG-1) through touba.project.json or --package, without registry resolution or implicit authority;
  • run plans (RP-1) through touba prepare --run-plan and touba run --run-plan, without sensitive payloads or automatic discovery;
  • binding fragments (BF-1) through touba prepare --binding-fragment, composed into the assembled manifest without direct fragment execution;
  • resource bindings (RB-1 / RB-2) through touba prepare --resource-bindings and touba run --resource-bindings, then transmitted to concerned handlers with host-provided backends.

The project contract remains explicit: no aliases, wildcards, re-exports, inline handlers in .tb, automatic manifest discovery by the runtime, automatic .env reads, or silently added grants or secrets.

These boundaries prevent an imported or prepared file from adding invisible execution authority. For tools, import makes the tool available while manifest, handler, grants, secrets, constraints, supply-chain lock, and runtime refusals remain explicit. For agents and memories, callable_by rules and scopes remain verified by project assembly.

Host decisions from a file and by module​

touba prepare --mode plan --host-decisions host-decisions.json
touba prepare --mode write --host-decisions host-decisions.json
touba prepare --mode check --host-decisions host-decisions.json
host-decisions.json
{
"modules": {
"orders.review": { "grants": ["network"], "timeout_ms": 5000 }
},
"bindings": {
"tool:orders.review/ReadOrder": {
"effect_contract": { "kind": "none" }
}
}
}

A module key matches its exact declared name. Module decisions cover grants, secrets and limits; each tool receives only grants and handles it requests. Binding decisions override the corresponding module decision, including an empty list to remove a grant. Effect contracts remain explicit per binding.

For multiple workflows, keep entry as the default and add entrypoints in touba.project.json. The project server and touba run --project . --entry <reference> use that same list.

Remove an unused binding​

After removing a tool from source, explicitly request removal of its old binding:

host-decisions.json
{"remove_bindings":["tool:orders.review/OldTool"]}
touba prepare --mode plan --host-decisions host-decisions.json
touba prepare --mode write --host-decisions host-decisions.json
touba prepare --mode check

The plan shows remove_binding. A tool still declared in source is protected; remove a fragment-owned binding from that fragment. Handler files remain for your review. Report imports are grouped by source relationship, so preparing several entry points keeps each relationship once.