@@ -15,6 +15,9 @@ The AI agent ecosystem is producing many sandbox environment providers — Dayto
1515- ** Provider behaviour** — a single contract for creating, destroying, and querying sandbox environments
1616- ** Process execution** — run commands in sandboxes with structured results
1717- ** File operations** — read, write, and list files within sandboxes
18+ - ** Named providers** — configure multiple providers with their credentials, pick a default
19+ - ** Local provider** — built-in provider for dev/test that runs everything on the local machine
20+ - ** Serialization** — persist and restore sandbox references across client restarts
1821- ** Provider-agnostic** — swap providers without changing application code
1922
2023## Installation
@@ -29,6 +32,46 @@ def deps do
2932end
3033```
3134
35+ ## Configuration
36+
37+ Configure multiple providers and set a default, similar to Finch pools:
38+
39+ ``` elixir
40+ # config/runtime.exs
41+ config :terrarium ,
42+ default: :daytona ,
43+ providers: [
44+ daytona: {Terrarium .Daytona , api_key: System .fetch_env! (" DAYTONA_API_KEY" ), region: " us" },
45+ e2b: {Terrarium .E2B , api_key: System .fetch_env! (" E2B_API_KEY" )},
46+ local: Terrarium .Providers .Local
47+ ]
48+ ```
49+
50+ Connect to an existing machine via SSH:
51+
52+ ``` elixir
53+ config :terrarium ,
54+ default: :server ,
55+ providers: [
56+ server: {Terrarium .Providers .SSH ,
57+ host: " dev.example.com" ,
58+ user: " deploy" ,
59+ auth: {:key , System .fetch_env! (" SSH_PRIVATE_KEY" )}
60+ }
61+ ]
62+ ```
63+
64+ For development, use the built-in local provider:
65+
66+ ``` elixir
67+ # config/dev.exs
68+ config :terrarium ,
69+ default: :local ,
70+ providers: [
71+ local: Terrarium .Providers .Local
72+ ]
73+ ```
74+
3275## Quick Start
3376
3477### 1. Add a provider package
4588### 2. Create and use a sandbox
4689
4790``` elixir
48- # Create a sandbox
49- {:ok , sandbox} = Terrarium .create (Terrarium .Daytona ,
50- image: " debian:12" ,
51- resources: %{cpu: 2 , memory: 4 }
52- )
91+ # Uses the configured default provider
92+ {:ok , sandbox} = Terrarium .create (image: " debian:12" )
93+
94+ # Or use a specific named provider
95+ {:ok , sandbox} = Terrarium .create (:e2b , image: " debian:12" )
96+
97+ # Or pass a provider module directly
98+ {:ok , sandbox} = Terrarium .create (Terrarium .Daytona , image: " debian:12" , api_key: " ..." )
5399
54100# Execute commands
55101{:ok , result} = Terrarium .exec (sandbox, " echo hello" )
@@ -63,6 +109,21 @@ IO.puts(result.stdout)
63109:ok = Terrarium .destroy (sandbox)
64110```
65111
112+ ### 3. Surviving client restarts
113+
114+ Sandboxes can be serialized and restored if the client process restarts while the remote sandbox is still running:
115+
116+ ``` elixir
117+ # Persist before shutdown
118+ data = Terrarium .Sandbox .to_map (sandbox)
119+ MyStore .save (" sandbox-123" , data)
120+
121+ # Restore after restart
122+ data = MyStore .load (" sandbox-123" )
123+ sandbox = Terrarium .Sandbox .from_map (data)
124+ {:ok , sandbox} = Terrarium .reconnect (sandbox)
125+ ```
126+
66127## Implementing a Provider
67128
68129Providers implement the ` Terrarium.Provider ` behaviour:
@@ -88,6 +149,12 @@ defmodule MyProvider do
88149 :running
89150 end
90151
152+ @impl true
153+ def reconnect (sandbox) do
154+ # Verify the sandbox is still alive, refresh tokens, etc.
155+ {:ok , sandbox}
156+ end
157+
91158 @impl true
92159 def exec (sandbox, command, opts) do
93160 # Execute the command
@@ -111,12 +178,44 @@ end
111178
112179| Provider | Package | Status |
113180| ---| ---| ---|
181+ | Local | ` terrarium ` (built-in) | Available |
182+ | SSH | ` terrarium ` (built-in) | Available |
114183| [ Daytona] ( https://daytona.io ) | ` terrarium_daytona ` | Planned |
115184| [ E2B] ( https://e2b.dev ) | ` terrarium_e2b ` | Planned |
116185| [ Modal] ( https://modal.com ) | ` terrarium_modal ` | Planned |
117186| [ Fly Sprites] ( https://sprites.dev ) | ` terrarium_sprites ` | Planned |
118187| [ Namespace] ( https://namespace.so ) | ` terrarium_namespace ` | Planned |
119188
189+ ## Telemetry
190+
191+ Terrarium emits telemetry events for all operations via ` :telemetry.span/3 ` . Each operation emits ` :start ` , ` :stop ` , and ` :exception ` events automatically.
192+
193+ | Event | Metadata |
194+ | ---| ---|
195+ | ` [:terrarium, :create, *] ` | ` %{provider: module} ` |
196+ | ` [:terrarium, :destroy, *] ` | ` %{sandbox: sandbox} ` |
197+ | ` [:terrarium, :exec, *] ` | ` %{sandbox: sandbox, command: string} ` |
198+ | ` [:terrarium, :read_file, *] ` | ` %{sandbox: sandbox, path: string} ` |
199+ | ` [:terrarium, :write_file, *] ` | ` %{sandbox: sandbox, path: string} ` |
200+ | ` [:terrarium, :ls, *] ` | ` %{sandbox: sandbox, path: string} ` |
201+ | ` [:terrarium, :reconnect, *] ` | ` %{sandbox: sandbox} ` |
202+ | ` [:terrarium, :status, *] ` | ` %{sandbox: sandbox} ` |
203+
204+ ``` elixir
205+ :telemetry .attach_many (
206+ " terrarium-logger" ,
207+ [
208+ [:terrarium , :create , :stop ],
209+ [:terrarium , :exec , :stop ],
210+ [:terrarium , :destroy , :stop ]
211+ ],
212+ fn event, measurements, metadata, _config ->
213+ Logger .info (" #{ inspect (event)} took #{ measurements.duration } native time units" )
214+ end ,
215+ nil
216+ )
217+ ```
218+
120219## License
121220
122221This project is licensed under the [ MIT License] ( LICENSE ) .
0 commit comments