Permissions

A Coil program gets no access to files, the network, environment variables or other processes unless its build grants it. The grant is a compiler flag, and the compiler checks it: every host call that main or a test can reach must be covered, or the build fails with the flag to add.

$ coil load.hy
Error: [E0414] `io::open` requires `--allow-read`: reached from `main` → `load`
Help: pass `--allow-read` (or `-A` for everything)

$ coil --allow-read load.hy

Capabilities#

FlagGrantsCalls
--allow-readRead files and inspect the file systemio::open for reading, io::fs::exists / is_file / is_dir / is_symlink / metadata / read_link / list_dir / realpath, io::fs::copy
--allow-writeCreate, change and remove filesio::open for writing, io::fs::create_dir / create_dir_all / remove_file / remove_dir / remove_dir_all / rename / symlink, io::fs::copy
--allow-netOpen socketsio::net::tcp::connect / connect_timeout / listen, io::net::udp::bind / connect / send_to
--allow-envEnvironment variables and the working directoryenv::var / set_var / remove_var / set_cwd
--allow-execRun other programsenv::exec (error E0406)
--allow-exitEnd the process earlyenv::exit (error E0407)
--allow-attachHand a raw descriptor to a streamStream.attach (error E0408)
--allow-ffi-execProcess-exec symbols through FFIsystem, execve, …
-A, --allow-allAll of the above

Read, write, net and env report E0414. dload is separate: each library needs --allow-dload STEM (see FFI), and -A does not grant it. At run time the stem also needs --dload-pin STEM=SHA256 or --dload-trusted STEM, or the load is denied.

io::open needs what its mode needs when the mode is a string literal: "r" needs read; "w" and "a" need write; "rw" needs both. A mode computed at run time needs both. Any other mode is Err(IoError::InvalidInput).

Stream IO on something already open (read_to_end, write_all, a socket’s send) needs no flag: the grant is checked where the file or socket is opened.

What counts#

Only calls the program can reach count. The compiler follows calls from main, from each test("…") case under coil test, and from static initializers. Importing a module that has an env::exec helper you never call needs no grant. When a call is reached, the error names the chain of functions that reaches it, so you can see why.

A library build with no main and no tests needs no grants. Its users grant what their programs reach.

Macros never get host access: a host call inside a macro fails when the macro runs, whatever the flags.

The compiled program is the grant#

Flags are read when the program is compiled. coil run out.hyc and packaged executables run what was compiled and do not take the flags again. Grants are not recorded in the archive and coil does not read them from coil.toml. The dload pins and trusted stems are the exception: they are checked when the library loads, so coil run out.hyc takes --dload-pin / --dload-trusted; a packaged executable uses the hashes in its embedded native lock.

In a spool project#

spool reads [permissions] from coil.toml and passes the flags to every coil command it runs (spool run, spool test, spool build, the editor server):

[permissions]
read = true
write = true
net = true
# env, exec, exit, attach: also true / false
# all = true   # everything, like -A

A key left out is denied. spool also passes [ffi] allow, the coil.lock native pins and trusted deps as --allow-dload, --dload-pin and --dload-trusted. See Project configuration.

↑ ↓ navigate ↵ open