Apps#
An app is someone else's code, running inside Ohm, with a list of what it is allowed to reach. It is a folder with a manifest in it:
apps/
hello-world/
ohm.app.json who it is, what it adds, what it needs
start.ohm runs once when the app is switched on
summarise.ohm a command
actions.ohm another command
apps/ is inside the scripts folder (Settings → Folders says where that is). Ohm scans it
on start and whenever you press Rescan in Settings → Apps. There is no registry, no
install step and no database that can disagree with the disk: the folders are the list.
Ohm ships one app, Hello World, seeded like any other example. Open
apps/hello-world/ in the Scripts tree and read it — it is 40 lines, and copying it is the
intended way to start.
What an app can add#
| It can | How |
|---|---|
| Put commands in Settings → Apps, runnable with one click | commands in the manifest |
| Run something once when it is switched on | onStart |
| Read and drive pages, call the model, write to the Results panel | the capabilities it asks for |
Commands are ordinary Ohm, Volt or Bolt scripts. Nothing about an app's code is special; what is special is that it runs with a capability list.
The manifest#
{
"id": "hello-world",
"name": "Hello World",
"version": "1.0.0",
"description": "One sentence a person reads before trusting it.",
"author": "You",
"homepage": "https://github.com/you/hello-world",
"capabilities": ["page", "ai", "results"],
"commands": [
{
"title": "Summarise this page",
"icon": "◉",
"description": "Reads the active tab and puts a summary in Results.",
"script": "summarise.ohm"
}
],
"onStart": "start.ohm"
}
| Field | Required | Notes |
|---|---|---|
id |
yes | Lower-case letters, digits and dashes, 3–40 characters. Must equal the folder name. |
name |
yes | What is shown. |
version |
yes | major.minor.patch. |
description |
no | One sentence, in plain words. It is the first thing anyone reads. |
author, homepage |
no | Shown in Settings. |
capabilities |
no | See below. Omitted means none, which is a real and useful choice. |
commands |
no | title and script are required; icon and description are not. The script must be inside the app folder. |
onStart |
no | A script run once per start. Keep it quick and quiet. |
The manifest is parsed strictly. An unknown capability is an error rather than something
quietly dropped — an app asking for filesystem when the capability is files must not
end up looking like it asked for nothing at all. A manifest that does not parse still
appears in Settings, with the reason, because an app that silently fails to appear is worse
than one that says why.
Capabilities#
A script running as part of an app may only call the host functions its capabilities cover.
| Capability | Risk | What it allows |
|---|---|---|
page |
high | See which tabs are open, read any page’s text, structure and form values; click, type and run JavaScript in it. |
remote |
high | Send data to an endpoint you configured. Refused entirely while the remote switch is off. |
tabs |
medium | Open a page, navigate it, show or hide it, close it. Not read it. |
ai |
medium | Send text to the configured model profile and read the reply. |
files |
medium | Read and write inside the scripts folder. Nowhere else. |
projects |
medium | Read and change prompts, curves, experiments, graphs, specs and documents. |
models |
medium | List local models, search Hugging Face, download model files. |
library |
low | Read the prompts, functions and view templates saved in Settings. |
results |
low | Render templates into the Results panel. |
Functions that cannot reach anything — sleep, json.encode, print, the ohm.*
concurrency helpers — need no capability at all.
Settings → Apps expands every capability into the exact host functions it permits, and that list is read from the host itself rather than written out by hand, so the page cannot drift away from what is enforced.
How it is enforced#
There is exactly one way out of a script — ScriptHost.call — and a run inside
apps/<id>/ carries that app's id. So a capability either appears on the list or the call
does not happen:
"hello-world" tried to browser.open() but did not ask for the "tabs" capability
(open and close tabs). Add it to the app's ohm.app.json, or stop the app in
Settings -> Apps.
Two things follow from this that are worth knowing:
- A stopped app is granted nothing. Stopping is not cosmetic; every call is refused.
- Finding a page is reading, not opening.
browser.active()andbrowser.tabs()needpage, nottabs: an app that only wants to read the page in front of you must not have to ask for the power to open tabs to get hold of it. - Capabilities are per-run, from the path. A script you wrote in the scripts folder is
not an app and is not restricted. Moving a script into
apps/<id>/restricts it.
Managing apps#
Settings → Apps lists everything installed. For each app:
- its name, version, author and description, and whether it is running or stopped
- the capabilities it asks for, worst first, coloured by risk
- What it can reach — its folder, its menu entries, whether anything runs by itself, and the complete list of host functions it may call
- Start / Stop — stopping takes its capabilities away immediately
- Uninstall — the folder is moved to
.ohm/trashin the scripts folder, like any other delete, so an uninstall is recoverable - a button per command, to run it
Below the list, What the capabilities mean is the same reference as the table above, generated from the host.
Which apps are switched off is the only thing Ohm remembers about them; it lives in settings, so an app you stopped stays stopped across a rescan and a restart.
Publishing#
An app is a folder. Zip it, put it in a git repository, send it to someone — there is no
registry and nothing to sign up for. To install one, put the folder in apps/ and press
Rescan.
Before you publish:
- Ask for the fewest capabilities that work. They are the first thing anyone reads,
and
pagemeans "can read every page you visit while I am running". - Say what it does in
description, in one sentence. - Version it, and change the version when you change the app.
- Keep
onStartfast and quiet. It runs on every launch, and an app that announces itself twice is an app people turn off. - Keep every script inside the folder. A command pointing outside it is refused.
- Fail with a sentence a person can act on. Your error messages are the app's interface too.
apps/README.md, seeded next to the example, is the same standard in short form — it
travels with the app folder, which is where someone writing one will look.
From a script#
Apps are managed over the protocol, so a script or another app with the projects
capability cannot touch them; only the UI can. The methods are apps.list,
apps.refresh, apps.setEnabled, apps.uninstall, apps.run and apps.capabilities —
see the protocol reference.