Skip to content
β—‡ AirawatOS Developer
🚧 Under development β€” this portal is being built track by track. Content will keep growing, and some sections are still placeholders.

Build

Anatomy of a component

A component is a small folder with two things: an entrypoint (your code) and a description file (meta.yaml). The twist that makes it easy: the code is the source of truth for the contract. You write the code; aos check derives what the component reads, writes, calls, and needs β€” and writes it into meta.yaml for you.

1 · The entrypoint (your code)

You write ordinary Python against the SDK’s one I/O object, ctx. A driver marks its entrypoint with @driver.main and runs to completion:

from aos import driver, ctx, egress

egress("api.sensors.example.gov", rate_per_min=120, secrets=["SENSOR_API_KEY"])

@driver.main
def run():
    aoi  = ctx.get(AOI_ID, "space.aoi")                    # a READ  -> space.aoi
    resp = ctx.fetch("https://api.sensors.example.gov/v1/readings")  # a CALL (broker injects SENSOR_API_KEY)
    ctx.write_many(resp.json()["rows"],                    # a WRITE -> air.sensor_reading (bitemporal)
                   valid_from_field="observed_at")

An engine exposes named actions instead β€” each function marked with @engine.verb, naming the governed interface it implements:

from aos import engine, ctx

@engine.verb("hr.leave_apply@0.1.0")     # this action implements a governed interface
def request(req):
    ctx.write(leave_record(req))          # uses the same ctx surface

@engine.verb("hr.leave_decide@0.1.0")
def decide(req):
    ...

ctx is your whole safe toolkit β€” get/facts to read, write/write_many to write, fetch to call an allowed site (the broker injects declared secrets server-side), file for blobs, ai to ask the model. It can only do what the code declared, and the platform enforces that at run time.

2 · The derived contract (meta.yaml)

You never hand-write what the component touches. aos check reads the code above and derives this β€” you keep only the identity and non-derivable packaging at the top:

component_id: ds:airawatdeveloper:driver/air-sensors   # identity β€” you set this
kind: driver                                          # driver | engine | app | solution | ...
version: 0.2.0
entrypoint: run.py
# -- everything below is DERIVED from the code by `aos check` --
reads:   [space.aoi]                    # from ctx.get / ctx.facts
writes:  [air.sensor_reading]           # from ctx.write / ctx.write_many
scopes:                                 # reads/writes are the human summary;
  - { verb: write, scope: air.sensor_reading }   #   scopes are the exact grants the kernel enforces
egress:                                 # from egress(...) + ctx.fetch
  - { host: api.sensors.example.gov, rate_per_min: 120 }
secrets: [SENSOR_API_KEY]               # from egress(secrets=...); supplied from the vault at run time

Because those lists are derived, they can’t drift from what the code actually does β€” they are what the code does. The only things you keep by hand are identity and packaging the code can’t imply: the runtime it needs, its config schema, any bundled assets, and optional subscribes: (events it reacts to β€” the platform auto-wires each into a subscription at install).

For an engine, aos check also derives a verbs: block from the @engine.verb decorators β€” a map from each action to the governed interface it implements. Publishing the engine self-registers these into platform.verb_binding, governed data the domain-blind host reads to route each request to your engine. No hardcoded list, no deploy.

Build one hands-on in the Quickstart; the full ctx surface is covered in Working with governed data.