Clojure SDK for Pocketenv
1# pocketenv-clojure
2[](https://github.com/pocketenv-io/pocketenv-clojure/actions/workflows/ci.yml)
3[](https://clojars.org/io.pocketenv/pocketenv)
4
5Clojure SDK for the [Pocketenv](https://pocketenv.io) sandbox platform.
6
7## Installation
8
9Add to your `deps.edn`:
10
11```clojure
12{:deps io.pocketenv/pocketenv {:mvn/version "0.1.1-SNAPSHOT"}}
13```
14
15## Configuration
16
17Set one of the following before making any calls:
18
19| Method | Details |
20|-----------------|------------------------------------------------|
21| Env var | `POCKETENV_TOKEN=<your-token>` |
22| Token file | `~/.pocketenv/token.json` → `{"token": "..."}` |
23| Per-call option | Pass `:token "..."` in the opts map |
24
25Optional env vars:
26
27```
28POCKETENV_API_URL — API base URL (default: https://api.pocketenv.io)
29POCKETENV_PUBLIC_KEY — server public key for client-side encryption (hex-encoded)
30```
31
32## Quick start
33
34```clojure
35(require '[pocketenv-io.pocketenv :as pocketenv]
36 '[pocketenv-io.sandbox :as sandbox])
37
38;; Create, start, run a command, then clean up
39(-> (pocketenv/create-sandbox "my-box")
40 (sandbox/start)
41 (sandbox/wait-until-running)
42 (sandbox/exec "echo" ["hello from pocketenv"])
43 ;; {:ok #ExecResult{:stdout "hello from pocketenv\n" :stderr "" :exit-code 0}}
44 )
45```
46
47All functions return `{:ok value}` on success or `{:error reason}` on failure. The `sandbox/*` operations also accept an `{:ok sandbox}` result directly so they can be chained with `->` without unwrapping.
48
49---
50
51## Sandbox lifecycle
52
53### Create
54
55```clojure
56;; Minimal — uses the default openclaw base image on Cloudflare
57(pocketenv/create-sandbox "my-box")
58;; => {:ok #Sandbox{:id "..." :name "my-box" :status :stopped ...}}
59
60;; With options
61(pocketenv/create-sandbox "my-box"
62 {:provider "daytona"
63 :repo "https://github.com/acme/my-app"
64 :keep-alive true})
65```
66
67### Start / Stop / Delete
68
69```clojure
70(def sb (pocketenv/create-sandbox "my-box"))
71
72(sandbox/start sb)
73(sandbox/stop sb)
74(sandbox/delete sb)
75```
76
77### Wait until running
78
79```clojure
80(-> (pocketenv/create-sandbox "ci-runner")
81 (sandbox/start)
82 (sandbox/wait-until-running {:timeout-ms 120000 :interval-ms 3000}))
83```
84
85---
86
87## Running commands
88
89```clojure
90(let [result (-> (pocketenv/get-sandbox "my-box")
91 (sandbox/exec "ls" ["-la" "/"]))]
92 (println (get-in result [:ok :stdout])))
93
94;; Run a Clojure test suite
95(-> (pocketenv/get-sandbox "my-box")
96 (sandbox/start)
97 (sandbox/wait-until-running)
98 (sandbox/exec "clojure" ["-M:test"]))
99;; => {:ok #ExecResult{:stdout "..." :stderr "" :exit-code 0}}
100```
101
102`ExecResult` fields: `:stdout`, `:stderr`, `:exit-code`.
103
104---
105
106## Listing sandboxes
107
108```clojure
109;; Public sandbox catalog
110(pocketenv/list-sandboxes)
111;; => {:ok {:sandboxes [...] :total 42}}
112
113;; With pagination
114(pocketenv/list-sandboxes {:limit 10 :offset 20})
115
116;; Sandboxes belonging to a specific actor (DID or handle)
117(pocketenv/list-sandboxes-by-actor "alice.pocketenv.io")
118(pocketenv/list-sandboxes-by-actor "did:plc:abc123")
119```
120
121---
122
123## Exposing ports
124
125```clojure
126;; Expose port 3000 and get a public preview URL
127(-> (pocketenv/get-sandbox "my-box")
128 (sandbox/expose 3000 {:description "Web server"}))
129;; => {:ok "https://preview.pocketenv.io/..."}
130
131;; List all exposed ports
132(-> (pocketenv/get-sandbox "my-box")
133 (sandbox/list-ports))
134;; => {:ok [#Port{:port 3000 :description "Web server" :preview-url "..."}]}
135
136;; Remove a port
137(-> (pocketenv/get-sandbox "my-box")
138 (sandbox/unexpose 3000))
139```
140
141---
142
143## VS Code Server
144
145```clojure
146(-> (pocketenv/get-sandbox "my-box")
147 (sandbox/vscode))
148;; => {:ok "https://preview.pocketenv.io/.../vscode/..."}
149```
150
151---
152
153## Secrets
154
155Secrets are encrypted client-side before transmission.
156
157```clojure
158;; Add a secret
159(pocketenv/add-secret "sandbox-id" "DATABASE_URL" "postgres://...")
160
161;; Or via the sandbox pipe
162(-> (pocketenv/get-sandbox "my-box")
163 (sandbox/set-secret "API_KEY" "sk-..."))
164
165;; List secrets (names only — values are never returned)
166(pocketenv/list-secrets "sandbox-id")
167;; => {:ok [#Secret{:id "..." :name "DATABASE_URL" :created-at "..."}]}
168
169;; Delete a secret by id
170(pocketenv/delete-secret "secret-id")
171```
172
173---
174
175## SSH keys
176
177Private keys are encrypted client-side before transmission.
178
179```clojure
180;; Store a key pair
181(pocketenv/put-ssh-keys "sandbox-id" private-key-str public-key-str)
182
183;; Or via the sandbox pipe
184(-> (pocketenv/get-sandbox "my-box")
185 (sandbox/set-ssh-keys private-key-str public-key-str))
186
187;; Retrieve
188(pocketenv/get-ssh-keys "sandbox-id")
189;; => {:ok #SshKey{:id "..." :public-key "..." :private-key "..." :created-at "..."}}
190```
191
192---
193
194## Tailscale
195
196```clojure
197;; Store a Tailscale auth key (must start with "tskey-auth-")
198(pocketenv/put-tailscale-auth-key "sandbox-id" "tskey-auth-...")
199
200;; Or via the sandbox pipe
201(-> (pocketenv/get-sandbox "my-box")
202 (sandbox/set-tailscale-auth-key "tskey-auth-..."))
203
204;; Retrieve
205(pocketenv/get-tailscale-auth-key "sandbox-id")
206;; => {:ok #TailscaleAuthKey{:id "..." :auth-key "..." :created-at "..."}}
207```
208
209---
210
211## Actor / profile
212
213```clojure
214;; Your own profile (requires authentication)
215(pocketenv/me)
216;; => {:ok #Profile{:did "did:plc:..." :handle "alice" :display-name "Alice" ...}}
217
218;; Any actor's profile
219(pocketenv/get-profile "alice.pocketenv.io")
220(pocketenv/get-profile "did:plc:abc123")
221```
222
223---
224
225## Error handling
226
227Every function returns `{:ok value}` or `{:error reason}`. Use `if-let` or pattern matching:
228
229```clojure
230(let [result (pocketenv/get-sandbox "does-not-exist")]
231 (if-let [sb (:ok result)]
232 (println "Found:" (:name sb))
233 (println "Error:" (:error result))))
234```
235
236When chaining with `sandbox/*`, passing `{:error ...}` into any operation throws an `ExceptionInfo`, keeping failures explicit.
237
238---
239
240## License
241
242MIT