23 KiB
Skald manual
Please see the file skald-install.md for installation instructions. Please see the section Server administration below for detailed administration instructions.
Usage
Skald includes a web server that, by default, listens on port 8443. Skald is therefore accessed on URLs such as:
https://skald.example.org:8443/
The landing page
There is a landing page at the root of the server. The landing page contains a list of all halls marked "public" in their configuration, as well as a form that allows joining an arbitrary hall.
Going through the landing page is not required: you are welcome to point your browser directly at the desired hall URL.
The hall pages
A hall named e.g. city-watch has an associated page
/hall/city-watch/. After login, it presents an interface consisting
of a user list, chat, and connection controls:
- the left side pane contains the list of users who have joined the hall; every username doubles as a menu of hall and user actions;
- the chat pane contains the messages published to the hall;
- the controls area contains login, connection, microphone, and settings controls.
On mobile, the interface is collapsed to fit the smaller screen. The top controls expose the user list, chat, and settings.
Buttons
There are up to two buttons at the top. Enable starts the microphone when no local microphone stream is active.
The Mute button mutes or unmutes the microphone; the microphone can be muted remotely by the hall moderator, but it cannot be unmuted remotely. To stop and release the local microphone stream, use Turn microphone off from the menu opened from your own name.
Side menu
There is a menu on the right of the user interface. It allows choosing the microphone, selecting whether to receive audio, and adjusting audio processing options.
User list
There is a user list on the left, starting with the current user and followed by all the users that have joined the current hall.
The user list doubles as a set of menus. Clicking on the current user (the first entry in the user list) opens the hall menu, a menu with actions that apply to the hall as a whole. Clicking on a different user opens a user menu, a menu that applies to that specific user.
For operators who are allowed to record, the hall menu includes the current recording control and an Open recordings (opens in new tab) action. Open recordings (opens in new tab) opens the hall's recording archive in a new tab, which lists completed recordings when there are multiple files.
Chat pane
The centre pane is a traditional chat interface, with an input form at the
bottom and the chat history above it. Chat history is never saved to
disk, and is erased after four hours (or whatever is specified in the
"max-history-age" field of the hall definition).
Double-clicking on a message opens a contextual menu.
The chat form doubles as a command-line interface, which is especially
important for visually-impaired users, and more generally is often faster
than navigating the user interface. Commands start with a slash character
"/". The most important command is /msg, which sends a private
message to a given user. Type /help to display the list of available
commands.
Below the chat input is a session-only virtual chalkboard. It is a
multiline text area suitable for commands, code, and notes that should be
visible to everyone in the hall. Operators and administrators may edit
the chalkboard and may grant or revoke chalkboard editing for connected
users with /chalkboard user and /unchalkboard user. Users without
edit permission can read and focus the chalkboard, but it is marked
read-only and does not trap the Tab key.
Inviting users
In order to generate an invitation link, choose the entry Invite user in the hall menu. This generates a link of the form
https://skald.example.org:8443/hall/city-watch/?token=XXX
where the XXX part, known as the token, is a shared secret. Such a link allows password-less login to the hall, and may therefore be shared e.g. over e-mail or instant messaging.
The invitation functionality is usually restricted to the moderator;
however, halls may be configured with the "unrestricted-tokens" option,
which allows all users to generate tokens.
Tokens can be created, modified, and expired using the /invite,
/reinvite, and /revoke commands.
File transfer
Skald includes a peer-to-peer, end-to-end encrypted file transfer protocol. In order to transfer a file, click on the receiver's entry in the user list and choose Send file.
Hall moderation
If a user has the op permission (short for operator), then they have access to a number of moderation tools. The contextual menu that opens when clicking on an entry in the user list is expanded with commands for muting a user, sending them a warning, retrieving their IP address, or kicking them out from a hall.
The hall menu (opened by clicking on one's own entry in the user's list) is extended with options to lock or to unlock a hall (a locked hall is one that non-operator users cannot join). Operators with recording permission can also start or stop recording and open the hall's recording archive from the hall menu.
All of the moderation commands are also available as command-line commands (see above), which is helpful when moderating large halls.
Server administration
The global configuration file
The server may be configured in the JSON file data/config.json. This
file may look as follows:
{
"users":{"vetinari": {"password":"lagniappe", "permissions": "admin"}},
"canonicalHost": "skald.example.org",
"writableHalls": true,
"recordingRetention": "168h"
}
or, better, with a hashed password:
{
"users": {
"vetinari": {
"password":{"type":"bcrypt","key":"$2a$10$bTWW..."},
"permissions": "admin"
}
},
"canonicalHost": "skald.example.org",
"writableHalls": true,
"recordingRetention": "168h"
}
The file is initially created using skaldctl initial-setup, but may be
manually edited at any time (there is no need to restart the server). The
fields are as follows:
-
usersdefines the users allowed to administer the server, and has the same syntax as user definitions in halls (see below), except that the only meaningful permission is"admin"; -
writableHalls: if true, then the HTTP administrative API can be used to modify hall definitions; if unset or false, then the HTTP API is read-only. This does not affect localskaldctlcommands; -
recordingRetention: if set, completed recording files older than this duration are automatically deleted from the recordings directory. The value uses Go duration syntax, for example"24h"for one day or"168h"for seven days. If unset, recordings are kept until deleted manually; -
allowOriginis an array containing the list of HTTP origins that are allowed to access the server; -
allowAdminOriginis likeallowOrigin, but applies to the HTTP administrative API; -
proxyURL: if running behind a reverse proxy, this specifies the root URL that will be visible outside the proxy; -
canonicalHost: the canonical name of the host running the server; clients that attempt to access the server using a different host name will be redirected to the canonical one.
Hall definitions
Halls are described by JSON files in the halls/ directory. These
files are normally administered using the skaldctl utility, but may
also be edited manually (there is no need to restart the server).
skaldctl edits these files directly and must be run on the server as a
user that may write the hall directory. By default it uses ./data and
./halls; use the global -data and -halls options for other paths.
Managing halls using skaldctl
Creating, modifying, and deleting halls
A hall is created using skaldctl create-hall:
skaldctl create-hall -hall city-watch
For a guided setup that asks for the hall name, common settings, operator accounts, and an optional shared participant password, use:
skaldctl create-hall -interactive
There are a number of options to customise the behaviour of the hall, see
skaldctl create-hall -help for a full list. For example, in order to
create a hall that allows unrestricted creation of tokens, say:
skaldctl create-hall -hall city-watch -unrestricted-tokens
For more advanced configuration, skaldctl create-hall can be invoked
with the -json flag, in which case it takes a JSON template from standard
input. The syntax of a JSON template is just like that of a hall
definition file (see below), except that it must not contain the fields
users and wildcard-user. For example, in order to create a redirect
(see the section Hall description reference below):
echo '{"redirect": "https://skald.example.org:8443/hall/city-watch/"}' | skaldctl create-hall -hall amcw -json
Halls are modified using skaldctl update-hall:
skaldctl update-hall -hall city-watch -unrestricted-tokens=false
If a JSON template is provided to skaldctl update-hall, then it is
merged with the existing hall configuration. Entries may be deleted
by setting them to null in the template:
echo '{"redirect": null}' | skaldctl update-hall -hall amcw
A hall is deleted using skaldctl delete-hall:
skaldctl delete-hall -hall amcw
Creating, modifying, and deleting users
A user entry is created with the skaldctl create-user command:
skaldctl create-user -hall city-watch -user Username -permissions op
If the -permissions flag is not specified, it defaults to present,
meaning that the user can participate in the chat and present audio to
the hall. Other useful values are message, which allows a user
to participate in the chat only, and observe, which doesn't allow any
active participation.
A user is modified using skaldctl update-user, and deleted using
skaldctl delete-user.
In order to be useful, a user entry needs to be assigned a password. This
is done with the skaldctl set-password command:
skaldctl set-password -hall city-watch -user Username
The fallback user
It is sometimes useful to allow multiple users to log in using the same password. This can be achieved by defining the wildcard user:
skaldctl create-user -hall city-watch -wildcard
skaldctl set-password -hall city-watch -wildcard
For open halls, where any user can login with any password, the wildcard
user's password is set to the password of type wildcard:
skaldctl set-password -hall city-watch -wildcard -type wildcard
See the section Client authorisation below for more information about password types.
Automatic subhalls
It is sometimes necessary to create a large number of identical halls. For example, the author has been using Skald to supervise computer science practicals, where up to 40 students are working in pairs.
While it is possible to automate the creation of halls by scripting calls to
skaldctl, by accessing Skald's HTTP administrative API, or by generating files
directly under halls/, Skald provides a facility called automatic
subhalls that can be used to generate halls on demand.
Automatic subhalls are enabled by setting the "auto-subhalls"
field in the hall description:
skaldctl create-hall -hall unseen-university -auto-subhalls
Whenever a user attempts to access a subhall of unseen-university, for
example unseen-university/hex, the hall is created in memory and
persists until it is empty and its chat history has expired. The main
hall's operator can view the list of populated subhalls with the command
/subhalls.
Managing tokens
Tokens are normally managed using the /invite, /reinvite, and /expire
commands in Skald's user interface, but they may also be managed using
the skaldctl utility's create-token, revoke-token, delete-token
and list-tokens commands:
skaldctl create-token -hall city-watch
skaldctl list-tokens -l -hall city-watch
A token that is generated with the -include-subhalls flag applies to
the whole hierarchy rooted at the given hall, including both ordinary
halls and automatically generated subhalls.
skaldctl create-token -hall city-watch -include-subhalls
Such a token can be attached to the root of the hall hierarchy, and therefore be valid for any hall on the server:
skaldctl create-token -hall '' -include-subhalls
Hall description reference
The definition for the hall called hallname is in the file
halls/hallname.json; it does not contain the hall name, which makes
it easy to copy or link hall definitions. You may use subdirectories:
a file halls/teaching/networking.json defines a hall called
teaching/networking.
Every hall definition file contains a single JSON dictionary. All fields are optional. The following fields are allowed:
-
users: a dictionary that maps user names to user descriptions (see below); -
wildcard-usera user description that will be used for usernames with no matching entry in theusersdictionary; -
authKeys,authServerandauthPortal: see Authorisation below; -
public: if true, then the hall is listed on the landing page; -
displayName: a human-friendly version of the hall name; this is displayed at the top of the hall page; -
description: a human-readable description of the hall; this is displayed on the landing page for public halls; -
contact: a human-readable contact for this hall, such as an e-mail address, ignored by the server; -
comment: a human-readable string, ignored by the server; -
max-clients: the maximum number of clients that may join the hall at one time; -
max-history-age: the time, in seconds, during which chat history is kept (default 14400, i.e. 4 hours); -
not-beforeandexpires: the times (in ISO 8601 or RFC 3339 format) between which joining the hall is allowed; -
allow-recording: if true, then recording is allowed in this hall; recordings are single mixed Ogg Opus audio files and requireffmpegon the server; -
unrestricted-tokens: if true, then ordinary users (without the "op" privilege) are allowed to create tokens; -
allow-anonymous: obsolete and ignored. Empty usernames are rejected; -
auto-subhalls: if true, then subhalls of the formhall/subhallare automatically created when first accessed; -
autolock: if true, the hall will start locked and become locked whenever there are no clients with operator privileges; -
autokick: if true, all clients will be kicked out whenever there are no clients with operator privileges; this is not recommended, prefer theautolockoption instead; -
redirect: if set, then attempts to join the hall will be redirected to the given URL; most other fields are ignored in this case; -
codecs: a list of codecs allowed in this hall, see below for possible values. The default is["opus"].
A user definition is a dictionary with entries password and
permission. The value of the password field is either a plaintext
password, or a hashed password generated for example by the skaldctl hash-password command. The value of the permissions field can either
be an array of individual permissions (not recommended), or one of the
following strings:
op: a hall operator, with all rights except administering the hall;present, an ordinary user with the right to publish audio streams and send chat messages;message: a user with the right to send chat messages;observe: a user that receives media streams and chat messages, but is not allowed to send them;admin: a user with the right to administer the hall (only).
Usernames must be non-empty and unique among connected non-system clients in a
hall. If a wildcard-user is configured, users may choose arbitrary names
that are not listed in users, but two connected users may not use the same
name.
The value of the codecs field is an array of codecs allowed in the
hall. Supported audio codecs include "opus", "g722", "pcmu" and
"pcma". Opus is the default and is the codec used by Skald's
single-file recording path.
Recording
When allow-recording is enabled and an operator starts recording, Skald
creates one recording file for the hall session under the configured
recordings directory. The file is named
skald-recording-YYYYMMDD-HHMMSS.ogg and contains a single mixed Ogg Opus
audio stream.
Skald uses one ffmpeg decoder process for each active incoming Opus RTP
audio source, mixes decoded 48 kHz stereo PCM every 20 ms, and sends the
mixed stream to one ffmpeg encoder process that writes the Ogg Opus file.
The mixer treats missing source frames as silence, applies deterministic
headroom and limiting before encoding, and logs recording metrics when the
file closes: active sources, decoder starts and stops, dropped source frames,
stale source ticks, dropped mixed frames, and encoder write errors.
The encoder is fed through a bounded queue so a stuck encoder cannot block
WebRTC packet handling indefinitely. If the queue fills, Skald drops mixed
frames and logs the condition. If ffmpeg is not installed or cannot be
started, recording fails with an operator-visible error instead of silently
creating a broken file.
The recorder closes when recording is stopped explicitly or when the hall no
longer has any non-system clients.
Maintainers can exercise the recording mixer and encoder without browser clients by running:
go run ./cmd/skald-recording-smoke -out /tmp -sources 5 -duration 10s
This creates a real mixed Ogg Opus file from synthetic PCM sources and prints the same core counters that are useful when checking larger halls.
If recordingRetention is set in data/config.json, Skald periodically
deletes completed recording files older than the configured duration.
Client Authorisation
Skald implements three authorisation methods: a username/password authorisation scheme, a scheme using stateful tokens, and a mechanism based on cryptographic tokens. The former two mechanisms are intended to be used in standalone installations, while the cryptographic mechanism is designed to allow easy integration with an existing authorisation infrastructure (such as LDAP, OAuth2, or even Unix passwords).
Password authorisation
When password authorisation is used, authorised usernames and passwords are
defined directly in the hall configuration file, in the users and
wildcard-user entries. The users entry is a dictionary that maps user
names to user descriptions; the wildcard-user is a user description
that is used with usernames that don't appear in users. These two
entries are usually managed by the skaldctl utility.
Every user description is a dictionary with fields password and
permissions. The password field may be a literal password string, or
a dictionary describing a hashed password or a wildcard. The
permissions field should be one of op, present, message or
observe. (An array of Skald's internal permissions is also allowed,
but this is not recommended, since internal permissions may vary from
version to version.)
For example, the entry
{
"users": {"Username": {"password": "Password", "permissions": "op"}}
}
specifies that user "Username" may log in as operator with password "Password", while
{
"wildcard-user": {"password": "1234", "permissions": "present"}
}
allows any username with the password 1234. Finally,
{
"wildcard-user":
{"password": {"type": "wildcard"}, "permissions": "present"}
}
allows any username with any password.
Hashed passwords
For security reasons, passwords are usually hashed before being stored in
hall descriptions (in fact, the skaldctl utility does not even support
storing plaintext passwords). A hashed password is represented as a JSON
dictionary with a field type and a number of type-specific fields.
A user entry with a hashed password looks like this:
"users": {
"Username": {
"password": {
"type": "pbkdf2",
"hash": "sha-256",
"key": "f591c35604e6aef572851d9c3543c812566b032b6dc083c81edd15cc24449913",
"salt": "92bff2ace56fe38f",
"iterations": 4096
},
"permissions": "op"
}
}
Hashed passwords are normally generated transparently to the user by the
skaldctl set-password command. When editing hall description files
manually, hashed passwords can be generated with the skaldctl hash-password
utility.
Stateful tokens
Stateful tokens are created by the /invite command in the Skald user
interface or by the skaldctl create-token command; see the section
Managing tokens above. They are stored in the file
data/var/tokens.jsonl, which, on most filesystems, can be safely backed
up without stopping the server.
Cryptographic tokens
In many cases, it is useful to delegate authorisation decisions to a third party, such as an LDAP or OAuth2 client. Skald implements delegation of authorisation decisions using cryptographic tokens generated by a third party known as an authorisation server. Two authorisation servers are available: an LDAP client, and a sample server written in Python.
When an authorisation server is used, the "authKeys" entry of the hall
configuration file specifies one or more public keys in JWK format (with
the restriction that the "alg" key must be specified explicitly):
{
"authKeys": [{
"kty": "oct",
"alg": "HS256",
"k": "MYz3IfCq4Yq-UmPdNqWEOdPl4C_m9imHHs9uveDUJGQ"
}, {
"kty": "EC",
"alg": "ES256",
"crv": "P-256",
"x": "dElK9qBNyCpRXdvJsn4GdjrFzScSzpkz_I0JhKbYC88",
"y": "pBhVb37haKvwEoleoW3qxnT4y5bK35_RTP7_RmFKR6Q"
}]
}
If multiple keys are provided, then they will all be tried in turn, unless the token includes the "kid" header field, in which case only the specified key will be used.
The hall file should also specify either an authorisation server or an
authorisation portal. An authorisation server is specified using the
"authServer" key:
{
"authServer": "https://auth.example.org"
}
If an authorisation server is specified, then the client, after it prompts for a password, will request a token from the authorisation server and join the hall using token authentication. The password is never communicated to the server.
Alternatively, the hall file may specify an authorisation portal using
the "authPortal" key. If an authorisation portal is specified, then the
default client will redirect initial client connections to the
authorisation portal. The authorisation portal is expected to authorise
the client and then redirect it to Skald with the username and token
query parameters set.