Subsystem

Part 6 of 6 Running one for other people

Adding the people

By the end of this chapter, Laurence and Keanu have accounts of their own on the team's instance, each with the way in they will actually use, and a screen in the office shows the board and can do nothing else.

The instance has been running since An account, an install and a first run with exactly one account on it, and that account is not a person. Putting that right is the first thing this chapter does, and it has to be done in a particular order.

The work so far

Laurence has the service account, the install and the instance of An account, an install and a first run, reached at the address TLS, a proxy and systemd gave it:

$ sudo useradd --system --no-create-home --shell /usr/sbin/nologin subroutine
$ sudo python3 -m venv /opt/subroutine
$ sudo /opt/subroutine/bin/pip install "subroutine[postgres]"
…
Successfully installed …
$ sudo install -d -o subroutine -g subroutine -m 0755 /var/lib/subroutine
$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine init --workspace metacortex --instance-name MetaCortex
Ready. Try: subroutine add "something to do"
$ sudo -u subroutine tee -a /var/lib/subroutine/config/subroutine/config.toml > /dev/null <<'SETTING'
public_url = "https://tasks.example.com"
SETTING

The account you already have is not a person

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user list
subroutine  person  instance admin

One row. subroutine init makes the first account and names it after whoever ran it, and on a server that was the service account. It administers the instance, it owns metacortex, and nobody can be it: it has no login shell, and nothing has ever issued it a credential.

subroutine user list without a workspace is the instance's accounts and what each may do to the installation itself. With a workspace it is that workspace's members and their roles there, which is the list further down.

It cannot be retired first

The tidy-minded move is to take the robot account out of the team's workspace before anybody else is in it. It is refused:

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user remove subroutine
metacortex would be left with nobody who can administer it.
Give somebody else an administrator's role there first. A workspace with no administrator cannot be repaired from inside it.

The same rule refuses subroutine user role, for the same reason: demoting the last administrator and removing them leave the workspace in the same state. A workspace nobody can administer has thrown away the remedy for every later mistake, including that one, and it cannot be repaired from inside.

So the order is fixed. Somebody who can administer the workspace comes first, and everything else follows.

A person who can administer it

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user create laurence --name "Laurence Fishburne" --role admin --browser
Created laurence
laurence is now admin in metacortex
Local commands will go on acting as subroutine.

A sign-in link for laurence, good for the next 30 minutes.

https://tasks.example.com/signin?link=sr_lnk_…

That is the only time it is shown, and it works once.

subroutine user create is one command doing three things: it makes the account, puts it in the workspace, and gives it a role there. --role is member unless you say otherwise, and --workspace can be left out while there is only one.

An account with no workspace can see nothing at all, which reads as a broken credential rather than a missing role. That is why the membership arrives with the account instead of waiting for a second command somebody has to remember.

--browser mints the sign-in link in the same breath, which is the other half of the work and the half that is easy to forget. There is no password, so an account nobody can get into is the ordinary result of stopping one command early.

That third line is about this server, not about Laurence. Adding a second account would otherwise leave the command line here unable to tell whose list to show, so it pins the one it had. It is printed once, on the first account added, and setting somebody up never takes something away from you.

Now leave the service account where it is. With Laurence able to administer the workspace, removing it would be allowed, and it would leave every command you run on this server belonging to no workspace and with nothing to write to. It costs one row in a list, and there is nothing else to be gained.

The rest of the team

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user create keanu --name "Keanu Reeves" --browser --terminal
Created keanu
keanu is now member in metacortex

A sign-in link for keanu, good for the next 30 minutes.

https://tasks.example.com/signin?link=sr_lnk_…

That is the only time it is shown, and it works once.

A credential for keanu, for the command line.

sr_…

That is the only time it is shown. Nothing recovers it afterwards.

Send it with this, which they run on their own machine:

  subroutine connections add metacortex --url https://tasks.example.com

…

--browser and --terminal are not alternatives. The link opens a browser session and the token is what a terminal and an agent present, so somebody who uses both doors needs both, and somebody having their machine set up by a colleague needs the token whatever they use.

The last lines are the one command Keanu runs at his end, which Your terminal here, your work there is the reader's side of. The name in it is his to choose, because it becomes the first part of every address he writes.

Send all of it the way you would send anything private. The link signs in as him and the token acts as him, and neither can be shown again.

Who is in the workspace, and what a role is

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user list --workspace metacortex
subroutine  owner
laurence    admin   Laurence Fishburne
keanu       member  Keanu Reeves

Roles belong to a workspace. member in one is not member in another: each workspace is seeded with its own, and this is the command that says who holds which. So an instance holding a second workspace is holding a second, separate answer to what somebody may do, and deciding that is its own permission rather than something an administrator has everywhere.

subroutine user add is how somebody who already has an account joins a second workspace. It takes --role rather than assuming one, because there is no reason a person's place in one workspace should decide their place in another.

Changing what somebody may do

Adding somebody and re-grading them are two decisions, so they are two commands:

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user role keanu admin
keanu is now admin in metacortex

It moves somebody who is already a member, and turns down somebody who is not, naming the command that would put them in:

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user role carrieanne member
There is no account called 'carrieanne' here.
Run 'subroutine user list' to see who there is.

The pair each name the other, so whichever you reach for first tells you the one you wanted.

When somebody leaves

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user remove keanu
keanu is no longer a member of metacortex

subroutine user remove takes away the membership and not the account:

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user list
subroutine  instance admin
laurence                    Laurence Fishburne
keanu                       Keanu Reeves

He is still there, with nothing he may do. Everything he wrote stays, and stays attributed to him, which is the point: a backlog that forgets who decided what is worth less than one that remembers, and somebody leaving is not a reason to rewrite the record.

Coming back is user add, and the role is said again rather than remembered:

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user add keanu --workspace metacortex --role member
keanu is now member in metacortex

He was an administrator when he left and he is a member now, because that is what was typed. A role is a decision about today rather than a property somebody keeps.

Handing a way in over afterwards

The two flags are a convenience, and the commands behind them stand on their own. This is what to run when somebody has lost a link, or is setting a second machine up:

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine login link --username keanu
A sign-in link for keanu, good for the next 30 minutes.

https://tasks.example.com/signin?link=sr_lnk_…

That is the only time it is shown, and it works once.

subroutine login link is also your own way back in. If the browser is how you administer this instance and something has gone wrong with it, a link minted at the console is a door that does not depend on anything else working. A stale one costs nothing: mint another.

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine token create --username keanu --title "Keanu's desktop"

sr_…

That is the only time it is shown. Store it now.
Give it to a client as SUBROUTINE_TOKEN, or add it to /var/lib/subroutine/config/subroutine/credentials.toml.

The title is what you will read in a list of credentials later, so name the machine rather than the person: one person with a desktop and a phone has two, and only the title tells them apart when one of them has to go.

That last line is advice for a credential you issued for yourself, and this one is for Keanu's machine rather than this one. The file it names is this server's.

--username and --service-account are separate flags because they are separate decisions. The first issues a credential for somebody who already has an account; the second is for a machine identity, and makes one if there is none. Naming a person under it is refused rather than quietly handing out their credential:

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine token create --service-account keanu
'keanu' is a person's account, not a machine identity.
  service_account: 'keanu' belongs to a person.
    Use '--username keanu' to issue a credential for them, or choose another name for the service account.

It is public_url, which TLS, a proxy and systemd set. Leave it unset and a link carries the address the instance is listening on, which on the arrangement this part builds is loopback: a working address on the server itself, and the wrong one to hand to anybody else.

An instance listening beyond this machine with nothing saying where it is reached has no address to put in a link at all, and says so rather than making one that cannot work:

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    SUBROUTINE_HOST=0.0.0.0 \
    SUBROUTINE_PUBLIC_URL= \
    /opt/subroutine/bin/subroutine login link --username keanu
This instance listens on '0.0.0.0', which is not an address a browser can be sent to, and no public_url says where it is reached instead.
Set public_url in config.toml to the address a browser reaches this instance on. A link is only useful where the web UI is served.

The two settings on that command put the instance back into the state it would be in without the previous chapter, which is the state a link is most often minted in by mistake: the proxy is up, the address works in a browser, and nothing has told Subroutine what it is.

A screen that only reads

A wall display, a kiosk, a board in a corridor: that is an account with the viewer role, and it needs nothing built.

$ sudo -u subroutine env \
    XDG_CONFIG_HOME=/var/lib/subroutine/config \
    XDG_DATA_HOME=/var/lib/subroutine/data \
    XDG_STATE_HOME=/var/lib/subroutine/state \
    /opt/subroutine/bin/subroutine user create screen --name "Office screen" --role viewer --browser
Created screen
screen is now viewer in metacortex

A sign-in link for screen, good for the next 30 minutes.

https://tasks.example.com/signin?link=sr_lnk_…

That is the only time it is shown, and it works once.

The browser hides the controls rather than refusing them. What a reader may do is worked out per workspace, and the page is drawn from that answer, so the capture box, Complete, Edit, the status and assignee controls and the comment box are absent. A control that appears and then says no is worse than one that is not there, and the instance refuses the write underneath it either way, as Signed in, from a browser or a phone shows from the other end.

Read-only is not inert. Controls that change how the page is read stay, such as revealing the rest of a truncated list, because that is a fact about the reader rather than about the item.

A screen nobody touches stays signed in. A session measures neglect rather than age: every use puts it back, so a display polling all day never lapses, while the same device left in a drawer eventually stops working.

Two things are worth knowing before you put one up, because neither is obvious and both are the cost of doing it this way:

Give it an account of its own rather than sharing somebody's. It can be revoked on its own, it says who did what if the screen is ever used to write, and turning it off locks nobody out.

With that, the people are in, each with the door they will use, and the instance is somebody's to administer other than the account that installed it.