Your server can read and write files on the user’s workstation through the context. Every call crosses the WebSocket to the client, which enforces what your server is allowed to touch.
Entitlements
By default a server may only read and write its own storage directory on the client: the per-server area Terminalwire also uses for session state, the same idea as a browser’s cookie jar. Every other path is denied until the user grants it with terminalwire-exec policy. The server asks; the client decides. It never reaches outside what the user has allowed.
Files
alias Terminalwire.Server.Context def export(ctx) do Context.file_write(ctx, "report.csv", MyApp.Reports.to_csv()) Context.puts(ctx, "Wrote report.csv") end
The file functions mirror what you’d expect:
Context.file_read(ctx, path) |
read the contents |
Context.file_write(ctx, path, content) |
write (replacing) |
Context.file_append(ctx, path, content) |
append |
Context.file_delete(ctx, path) |
delete |
Context.file_exists?(ctx, path) |
test for existence |
Directories
Context.dir_create(ctx, "exports") Context.dir_list(ctx, "exports") # => ["report.csv", …] Context.dir_exists?(ctx, "exports") Context.dir_delete(ctx, "exports")
A write to a path the user hasn’t granted fails rather than silently succeeding. Catch it and point them at the grant command instead of leaving a cryptic stack trace:
try do Context.file_write(ctx, path, data) Context.puts(ctx, "Saved #{path}") rescue _ -> Context.warn(ctx, "Can't write #{path}. Grant access with: terminalwire-exec policy") end