Things
Put a Thing on your computer, clone it, and publish a change.
Things are artifacts that your agent makes for you: files, webpages, or apps. Each page or app gets its own address, like itinerary.yourname.emptyos.com, and starts private. If you have made artifacts in a chat assistant, a Thing is an artifact that you own, on your own computer, and can be edited by any other agent or exported wherever you'd like.
A static Thing serves a file or a site. A service Thing runs an app, which can save to a Database. Every Thing keeps its history in Git, so you can undo its latest change.
$ empty put ./notes.html --as finance
Installed private static Thing finance from 1 file (61 bytes) at /home/exedev/things/finance
On yourname.emptyos.com
View it (owner sign-in): https://finance.yourname.emptyos.com/
Only you can open it until you share it
Share it: `empty share finance --with <email>`
Make it public: `empty expose finance`
See all Things: `empty things`
$ empty clone finance && cd finance
Cloning into 'finance'...
Bound /Users/you/finance to yourname:thing://finance
# edit, then
$ ./verify && git commit -am 'Add monthly view'
$ empty put . --json
When you put a Thing, put installs it as a Git repository at ~/things/<id> on the computer to track changes. clone copies that repository to your machine. You commit in the clone. put . from the clone publishes that commit.
Put (uploading)
put accepts a file, an HTML page, a static directory, or a repository with a Thing manifest. A service needs the manifest. Every file under the selected directory is served. Use a build output such as dist/ rather than a source root. EmptyOS does not scan for secrets.
Choose a descriptive ID when creating a Thing because it becomes the site's or
service's permanent hostname. For a Japan trip, use japan-itinerary rather
than itinerary or a build directory name:
empty put ./dist --as japan-itinerary --name "Japan Trip Itinerary"
Use --description "Reservations, transport, and daily plans" to add optional
text under the Thing's name in Home. Use a few words that add context beyond
the name; omit it when it would only repeat the name. Whitespace becomes
spaces, the limit is 200 characters, and empty text means no description.
For a manifested repository, commit description: in thing.yaml first;
the flag checks that value and does not rewrite it. Creation metadata flags
do not apply to put . from a bound clone. To change an existing description,
use empty thing set-description <id> <text...>.
An agent should honor an ID or URL you specify, otherwise choose a short name
that reflects the request and show the resulting link. It need not ask another
naming question when the context is clear. Direct imports without --as retain
the filename/directory-name default, lowercased. Invalid names or conflicts are
reported; an explicit ID is never silently renamed or used to overwrite a Thing.
Clone (local editing)
clone copies the Thing's Git repository to a local checkout bound to one computer and one Thing. Do not copy that checkout elsewhere. Do not edit its emptyos.* Git config keys.
Publish
put . from a clone publishes the commit you made. It requires a clean tree and exactly one commit on top of the live head. The command runs ./verify in the clone before sending the commit. The computer validates the candidate, activates it, and checks the route. On failure, the computer reverts the change and reports the failure.
A private static Thing can become a private service through the same put . workflow. Keep its ID and route, add the service code and manifest fields, and commit the change in the clone. Both the current Thing and candidate must be private with no restricted shares. Publication registers and starts the service, checks its route, and preserves Git history and Databases.
On a cloud-connected computer, sites and services use
<thing-id>.<owner-host> over HTTPS. For example,
https://yourname.emptyos.com/tracker/scores?q=1 redirects to
https://tracker.yourname.emptyos.com/scores?q=1. The Thing runs at / on its
own hostname, without the /tracker prefix. Its URL stays the same when shared,
made public or private, or converted from a private site to a service. Renaming
the display title does not change that address.
While private, the owner needs a live cloud-login session with verified email
to enter. A break-glass session permits owner management but requires cloud
reauthentication for Thing entry. Standalone computers retain private owner-path
hosting. Cloud computers do not fall back to owner-path hosting during an outage.
A prepared service can be exposed with empty expose <id> or shared with
empty share <id> --with <email>; see Sharing. Import services
privately first.
Publishing an existing service requires it to be registered and active. Forward service-to-static publication is unsupported. Undo of a conversion restores static serving at the same hostname; Redo registers and starts the service there again. The usual latest-change and clean-Thing checks apply, and neither action reverts Databases.
empty things and empty thing describe <id> show app admission separately
from the service process. A running process does not mean its public or private
app is admitted; closed or unavailable admission is shown explicitly.
Both commands print each Thing's URL (url in JSON). Sites and services keep
their per-Thing hostname across private, shared and public access. Files keep
their owner, shared and public path URLs. Share the link as printed. No URL is
shown when the computer's hostnames are unknown.
Deleting a site or service permanently reserves its hostname. A different Thing needs a
new ID, such as japan-itinerary-2; Git Undo does not release that reservation.
This prevents a new Thing from inheriting the old Thing's browser storage.
Prepare and verify
When you put a file, an HTML page, or a static directory, put adds thing.yaml and an executable ./verify to the new repository. That ./verify checks that thing.yaml and the expected served file exist. It also rejects a served file that is a symlink.
A repository with a Thing manifest needs a clean Git tree and a tracked executable ./verify. When the Thing needs to install declared dependencies, add a tracked executable ./prepare. put does not create ./prepare. Use a repository with a Thing manifest when you need this script.
On put and publish, the computer runs ./prepare first if it exists. ./prepare may install declared dependencies and use the network. Then the computer runs ./verify offline. ./verify exits 0 only when every check passes.
Service limits
Service apps have no outbound network access, so they cannot call external APIs. They also cannot receive external webhooks or use WebSockets. Build applications that work with the files and Databases on your computer. Dependency installation during ./prepare can use the network; the running service cannot.
Thing manifests
A repository becomes a Thing when it has thing.yaml at its root. For a static site, use a manifest like this:
id: finance
name: Finance
type: static
directory: dist
For a service, use a manifest like this:
id: tracker
name: Tracker
type: service
command: npm start
port: 4310
data:
- ledger
The manifest fields have these requirements:
idmust equal the directory name and use 1–63 lowercase ASCII letters, digits or hyphens, starting and ending with a letter or digit. Underscores, spaces and dots are not allowed.public,shared,wwwandapp<N>are reserved.descriptionis optional: one line of at most 200 characters that Home shows under the name, such asFamily meal planning. To change it on an existing Thing, runempty thing set-description <id> <text...>; pass""to remove it.typeisstaticorservice.- Static
directorydefaults to.and must stay inside the Thing. Serve a build output, not the source root. - A service needs
command, launched withPORTin the environment. - A service needs
port, a unique integer from 1024 through 65535. - A service should bind its listener to
127.0.0.1or0.0.0.0on that port. Its private network has no outbound access. - A service is limited to 1 GiB of memory (
MemoryMax=1G), 128 processes or threads (TasksMax=128), and systemd's@system-servicesystem-call set. Exceeding a limit can stop the service; useempty thing logs <id>to inspect the failure. routeis optional and defaults to/<id>.projectis optional and names a Project.datais optional and lists Database ids. See Databases.visibilityisprivate(default) orpublic. Change it withempty exposeandempty unexpose, not by editing the manifest on the computer. Any direct write tothing.yamlthere, even one that restores the original, closes the site or service untilempty thing restart <id>reconciles it.
When you put a repository with a manifest, --as, --name, --project, and --public must match the manifest.
Home icon
Home draws icon.svg, the file next to thing.yaml, on the Thing's tile in place of the neutral app, site, or file glyph. It is an ordinary file, not a manifest field, so adding or redrawing one is a normal committed change that needs no restart. Draw a simple line picture on a 0 0 24 24 viewBox, with strokes in currentColor and no text, images, or scripts. Home keeps only the shape and draws it in its own ink color. A symbolic link or a file over 64 KiB is not drawn; empty things --json reports icon and an iconError that says why. When you share a Thing, its shared-link page shows the same icon if the file is under about 10 KiB, and the kind glyph otherwise. The icon reaches that page the next time the Thing is shared, added, activated, or restarted.