SCUA

How-to

Ask the user a question

Read a line the person types at the terminal, and read a password without showing it.

import sys

sys.write("Your name: ")
let name = sys.readline()
print(`hello, {name}`)
$ scua --allow-tty greet.scua
Your name: Ada
hello, Ada

#Reading the terminal needs a grant

sys.readline and sys.readpassword need --allow-tty. Without it they do not exist, and the compiler tells you so by name rather than failing at run time:

greet.scua:4: `sys.readline` requires the `tty` capability, which this run was not granted —
grant it with `--allow-tty` on the CLI, or the host's capability API when embedding

--allow-all and a capability file (tty = true under [capabilities]) grant it too.

The grant exists because the terminal is where a person types secrets. It is also never inherited by a partition: an actor cannot read the terminal even when the program that spawned it can. Read at the top level and pass the value in.

#Always guard for the end of input

Both readers return nil when there is no more input — a closed pipe, Ctrl-D, or a run with no terminal at all. That is not an error; it is what unattended looks like:

let name = sys.readline()
if name == nil then
  print("no input — running unattended")
else
  print(`hello, {name}`)
end

Skip the guard and len(name) faults the first time someone pipes your script into something. The same script then works in all three shapes:

$ scua --allow-tty greet.scua              # a person types
$ printf 'Ada\n' | scua --allow-tty greet.scua   # piped
$ scua --allow-tty greet.scua < /dev/null        # unattended

#Passwords

sys.readpassword reads a line with the terminal's echo turned off, so the secret is not displayed and does not reach your scrollback.

sys.write("Password: ")
let pw = sys.readpassword()
if pw == nil then
  print("no password given")
else
  print(`got {len(pw)} characters`)
end

Three things worth knowing.

Pasted-ahead input is discarded. If you paste name⏎secret⏎ at the first prompt, the terminal has already displayed the secret — it landed in the field above, which was still echoing. No program can un-display it. What SCUA does is refuse to use it: anything typed ahead is thrown away and you are prompted again. Re-type at the quiet prompt.

On a pipe there is no echo to suppress, so readpassword simply reads. printf 'pw\n' | prog works, which is what makes scripts testable.

The secret is an ordinary string. It lives in memory like any other value — it can be copied, printed if you print it, and saved if you save it. SCUA does not scrub it. Treat it the way you would in any language: use it, do not store it.

#Don't mix sys.readline with sys.stdin()

sys.stdin() hands you the whole of standard input as one string. On a pipe, both draw from the same bytes — so using both means one of them takes input the other expected, and which one wins depends on the order you call them.

Pick one. sys.stdin() for a payload piped in; sys.readline for a conversation.

#Timers keep running while you wait

Waiting for input does not freeze the program: host timers continue to run while the prompt sits there. What you cannot do is read from inside a spawned task or an actor handler — that is refused, because it would silently stall everything else running alongside it.

#See also