{"id":48372401,"url":"https://github.com/pocketenv-io/pocketenv-clojure","last_synced_at":"2026-04-08T20:04:15.950Z","repository":{"id":348710642,"uuid":"1199475381","full_name":"pocketenv-io/pocketenv-clojure","owner":"pocketenv-io","description":"Clojure SDK for Pocketenv","archived":false,"fork":false,"pushed_at":"2026-04-05T16:18:20.000Z","size":37,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-05T17:29:09.054Z","etag":null,"topics":["agent","clojure","microvm","sandbox"],"latest_commit_sha":null,"homepage":"https://pocketenv.io","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pocketenv-io.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-02T11:41:15.000Z","updated_at":"2026-04-05T16:17:27.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pocketenv-io/pocketenv-clojure","commit_stats":null,"previous_names":["pocketenv-io/pocketenv-clojure"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/pocketenv-io/pocketenv-clojure","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pocketenv-io%2Fpocketenv-clojure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pocketenv-io%2Fpocketenv-clojure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pocketenv-io%2Fpocketenv-clojure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pocketenv-io%2Fpocketenv-clojure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pocketenv-io","download_url":"https://codeload.github.com/pocketenv-io/pocketenv-clojure/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pocketenv-io%2Fpocketenv-clojure/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31483380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T17:22:55.647Z","status":"ssl_error","status_checked_at":"2026-04-06T17:22:54.741Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","clojure","microvm","sandbox"],"created_at":"2026-04-05T17:04:21.924Z","updated_at":"2026-04-06T18:00:30.643Z","avatar_url":"https://github.com/pocketenv-io.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# pocketenv-clojure\n[![ci](https://github.com/pocketenv-io/pocketenv-clojure/actions/workflows/ci.yml/badge.svg)](https://github.com/pocketenv-io/pocketenv-clojure/actions/workflows/ci.yml)\n[![Clojars Project](https://img.shields.io/clojars/v/io.pocketenv/pocketenv.svg)](https://clojars.org/io.pocketenv/pocketenv)\n\nClojure SDK for the [Pocketenv](https://pocketenv.io) sandbox platform.\n\n## Installation\n\nAdd to your `deps.edn`:\n\n```clojure\n{:deps io.pocketenv/pocketenv {:mvn/version \"0.1.3-SNAPSHOT\"}}\n```\n\n## Configuration\n\nSet one of the following before making any calls:\n\n| Method          | Details                                        |\n|-----------------|------------------------------------------------|\n| Env var         | `POCKETENV_TOKEN=\u003cyour-token\u003e`                 |\n| Token file      | `~/.pocketenv/token.json` → `{\"token\": \"...\"}` |\n| Per-call option | Pass `:token \"...\"` in the opts map            |\n\nOptional env vars:\n\n```\nPOCKETENV_API_URL     — API base URL (default: https://api.pocketenv.io)\nPOCKETENV_PUBLIC_KEY  — server public key for client-side encryption (hex-encoded)\nPOCKETENV_STORAGE_URL — storage base URL for file transfers (default: https://sandbox.pocketenv.io)\n```\n\n## Quick start\n\n```clojure\n(require '[pocketenv-io.pocketenv :as pocketenv]\n         '[pocketenv-io.sandbox   :as sandbox])\n\n;; Create, start, run a command, then clean up\n(-\u003e (pocketenv/create-sandbox \"my-box\")\n    (sandbox/start)\n    (sandbox/wait-until-running)\n    (sandbox/exec \"echo\" [\"hello from pocketenv\"])\n    ;; {:ok #ExecResult{:stdout \"hello from pocketenv\\n\" :stderr \"\" :exit-code 0}}\n    )\n```\n\nAll 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 `-\u003e` without unwrapping.\n\n---\n\n## Sandbox lifecycle\n\n### Create\n\n```clojure\n;; Minimal — uses the default openclaw base image on Cloudflare\n(pocketenv/create-sandbox \"my-box\")\n;; =\u003e {:ok #Sandbox{:id \"...\" :name \"my-box\" :status :stopped ...}}\n\n;; With options\n(pocketenv/create-sandbox \"my-box\"\n  {:provider   \"daytona\"\n   :repo       \"https://github.com/acme/my-app\"\n   :keep-alive true})\n```\n\n### Start / Stop / Delete\n\n```clojure\n(def sb (pocketenv/create-sandbox \"my-box\"))\n\n(sandbox/start sb)\n(sandbox/stop  sb)\n(sandbox/delete sb)\n```\n\n### Wait until running\n\n```clojure\n(-\u003e (pocketenv/create-sandbox \"ci-runner\")\n    (sandbox/start)\n    (sandbox/wait-until-running {:timeout-ms 120000 :interval-ms 3000}))\n```\n\n---\n\n## Running commands\n\n```clojure\n(let [result (-\u003e (pocketenv/get-sandbox \"my-box\")\n                 (sandbox/exec \"ls\" [\"-la\" \"/\"]))]\n  (println (get-in result [:ok :stdout])))\n\n;; Run a Clojure test suite\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/start)\n    (sandbox/wait-until-running)\n    (sandbox/exec \"clojure\" [\"-M:test\"]))\n;; =\u003e {:ok #ExecResult{:stdout \"...\" :stderr \"\" :exit-code 0}}\n```\n\n`ExecResult` fields: `:stdout`, `:stderr`, `:exit-code`.\n\n---\n\n## Listing sandboxes\n\n```clojure\n;; Public sandbox catalog\n(pocketenv/list-sandboxes)\n;; =\u003e {:ok {:sandboxes [...] :total 42}}\n\n;; With pagination\n(pocketenv/list-sandboxes {:limit 10 :offset 20})\n\n;; Sandboxes belonging to a specific actor (DID or handle)\n(pocketenv/list-sandboxes-by-actor \"alice.pocketenv.io\")\n(pocketenv/list-sandboxes-by-actor \"did:plc:abc123\")\n```\n\n---\n\n## Exposing ports\n\n```clojure\n;; Expose port 3000 and get a public preview URL\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/expose 3000 {:description \"Web server\"}))\n;; =\u003e {:ok \"https://preview.pocketenv.io/...\"}\n\n;; List all exposed ports\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/list-ports))\n;; =\u003e {:ok [#Port{:port 3000 :description \"Web server\" :preview-url \"...\"}]}\n\n;; Remove a port\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/unexpose 3000))\n```\n\n---\n\n## VS Code Server\n\n```clojure\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/vscode))\n;; =\u003e {:ok \"https://preview.pocketenv.io/.../vscode/...\"}\n```\n\n---\n\n## Secrets\n\nSecrets are encrypted client-side before transmission.\n\n```clojure\n;; Add a secret\n(pocketenv/add-secret \"sandbox-id\" \"DATABASE_URL\" \"postgres://...\")\n\n;; Or via the sandbox pipe\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/set-secret \"API_KEY\" \"sk-...\"))\n\n;; List secrets (names only — values are never returned)\n(pocketenv/list-secrets \"sandbox-id\")\n;; =\u003e {:ok [#Secret{:id \"...\" :name \"DATABASE_URL\" :created-at \"...\"}]}\n\n;; Delete a secret by id\n(pocketenv/delete-secret \"secret-id\")\n```\n\n---\n\n## SSH keys\n\nPrivate keys are encrypted client-side before transmission.\n\n```clojure\n;; Store a key pair\n(pocketenv/put-ssh-keys \"sandbox-id\" private-key-str public-key-str)\n\n;; Or via the sandbox pipe\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/set-ssh-keys private-key-str public-key-str))\n\n;; Retrieve\n(pocketenv/get-ssh-keys \"sandbox-id\")\n;; =\u003e {:ok #SshKey{:id \"...\" :public-key \"...\" :private-key \"...\" :created-at \"...\"}}\n```\n\n---\n\n## Tailscale\n\n```clojure\n;; Store a Tailscale auth key (must start with \"tskey-auth-\")\n(pocketenv/put-tailscale-auth-key \"sandbox-id\" \"tskey-auth-...\")\n\n;; Or via the sandbox pipe\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/set-tailscale-auth-key \"tskey-auth-...\"))\n\n;; Retrieve\n(pocketenv/get-tailscale-auth-key \"sandbox-id\")\n;; =\u003e {:ok #TailscaleAuthKey{:id \"...\" :auth-key \"...\" :created-at \"...\"}}\n```\n\n---\n\n## Copying files\n\nUpload a local file or directory into a sandbox, download a sandbox path to local disk, or copy directly between two sandboxes — all through storage, with no manual archiving required.\n\n```clojure\n;; Upload a local directory to the sandbox\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/upload \"./my-project\" \"/workspace\"))\n\n;; Upload a single file\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/upload \"./config.json\" \"/app/config.json\"))\n\n;; Download a sandbox path to a local directory\n(-\u003e (pocketenv/get-sandbox \"my-box\")\n    (sandbox/download \"/workspace/output\" \"./output\"))\n\n;; Copy a path from one sandbox to another (no local I/O)\n(-\u003e (pocketenv/get-sandbox \"source-box\")\n    (sandbox/copy-to \"dest-sandbox-id\" \"/workspace\" \"/workspace\"))\n```\n\nAll three functions return `:ok` on success or `{:error reason}` on failure and are pipe-safe — they accept a bare `Sandbox` or an `{:ok sandbox}` result from a previous step.\n\n---\n\n## Actor / profile\n\n```clojure\n;; Your own profile (requires authentication)\n(pocketenv/me)\n;; =\u003e {:ok #Profile{:did \"did:plc:...\" :handle \"alice\" :display-name \"Alice\" ...}}\n\n;; Any actor's profile\n(pocketenv/get-profile \"alice.pocketenv.io\")\n(pocketenv/get-profile \"did:plc:abc123\")\n```\n\n---\n\n## Error handling\n\nEvery function returns `{:ok value}` or `{:error reason}`. Use `if-let` or pattern matching:\n\n```clojure\n(let [result (pocketenv/get-sandbox \"does-not-exist\")]\n  (if-let [sb (:ok result)]\n    (println \"Found:\" (:name sb))\n    (println \"Error:\" (:error result))))\n```\n\nWhen chaining with `sandbox/*`, passing `{:error ...}` into any operation throws an `ExceptionInfo`, keeping failures explicit.\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpocketenv-io%2Fpocketenv-clojure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpocketenv-io%2Fpocketenv-clojure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpocketenv-io%2Fpocketenv-clojure/lists"}