Authentication#
This page describes the credential model of the sysand client for HTTP(S)
indexes and project downloads: where credentials come from, how they are
stored and scoped, and how sysand decides which credential to use for a
request.
For the conceptual overview, see
How authentication works. For
step-by-step setup, see
Authenticate to an index. For the
sysand auth command family, see
sysand auth. To create a Sysand Index API
token, see Create an API token.
Credential sources and precedence#
sysand takes credentials from two sources:
Stored credentials, written by
sysand auth logininto the operating system’s keyring. Bearer tokens only.SYSAND_CRED_*environment variables, described below. Bearer tokens or HTTP basic (username and password).
When both sources have a credential whose URL pattern matches a request,
the environment credential is tried first. This lets a CI job override an
interactive credential without removing it.
sysand auth status shows both sources and
marks stored entries that are shadowed by an environment variable.
Being tried first is not the same as being the only one tried. On an
ordinary read, if the environment credential is itself rejected, sysand
falls back to a matching stored credential rather than giving up, so
setting SYSAND_CRED_* does not guarantee that a stored token is never
sent to that host. sysand publish and
sysand auth whoami are the exceptions: each
selects exactly one credential and stops, so there the environment
credential really does displace the stored one.
Precedence decides which credential wins, not when it is sent: for
ordinary reads sysand still tries each URL without credentials first,
as described under Request behavior.
Stored credentials#
sysand auth login stores one bearer token per index. All stored credentials
live in a single entry in the operating system’s keyring: the macOS
Keychain, the Windows Credential Manager, or the Secret Service (for
example GNOME Keyring or KWallet) on Linux. sysand never writes
credentials to a plaintext file.
Along with the token, a stored credential records the index URL it belongs to,
the derived URL patterns it covers, the validation result, and, when a
validating login ran against an index with an API, the identity behind the
token (subject, token display prefix, and expiry). sysand auth status
shows all of this; no command ever prints the token itself.
Logging in again to the same index replaces the previous stored token (the old token is discarded locally, not revoked server-side). Removing the last credential deletes the keyring entry entirely.
The index URL doubles as the key of the stored credential, so sysand
normalizes it first: the host is lowercased, a default port is dropped,
and a missing trailing path slash is added. Different spellings of one
index therefore reach the same entry rather than creating duplicates, and
sysand auth status prints each key in the form sysand auth logout
expects. A URL template is
normalized the same way and kept verbatim as the key, placeholder and all,
which is separate from the narrower prefix it covers.
The URL must be an absolute http(s) URL, with no # fragment and no
username or password in it. A query string is allowed and stays part of the
key, which is what makes a forge URL such as ?ref=main usable as an
index.
The keyring is only read when a credential might actually be needed:
local commands and reads that succeed without authentication never touch
it. If the keyring exists but is locked or denies access, sysand
reports the error and suggests unlocking it or falling back to
SYSAND_CRED_* variables.
On Windows the keyring caps the stored blob at roughly 2.5 KB, measured in
UTF-16 units, so about 1280 characters of stored JSON. In practice that is
a handful of credentials, three or four with typical tokens, and a single
very large token can exceed it on its own. When the limit is hit,
sysand auth login fails and points you at sysand auth status and
sysand auth logout to remove one, naming any already-expired credentials
as the obvious candidates.
Hosts without a keyring#
On a host with no usable keyring backend (many containers, headless
servers, and CI runners), sysand auth login refuses to persist the
token and instead prints the exact SYSAND_CRED_* variables to set, with
the token replaced by a <token> placeholder so the secret never reaches
logs. Environment variables are the supported path on such hosts; note
that the secret then lives in same-user process environments and
typically at rest in CI secret configuration or shell startup files,
which is the accepted floor there.
Login validation#
sysand auth login always validates the token against the index before
storing it, by probing each surface the index has:
the read surface: the index file listing (
index.json), andthe index API (
v1/whoami), only when the index’s discovery configuration advertises an API root.
A surface only counts as tested when the probe actually exercised the
credential: a public read surface answers without credentials and proves
nothing about the token. Unreachable, redirected, or rate-limited probes
are not verdicts either. The token is stored unless every surface that
exercised it rejected it; the stored validation claim is scoped to what
actually accepted it, for example validated (read),
validated (read, api), or stored, not validated when nothing
exercised the credential (sysand auth status shows that last record more
briefly, as not validated).
Consequences worth knowing:
On an index with public reads and an API (such as sysand.com), the API is the only real test, so a mistyped or expired token is refused at login time rather than failing later at publish time.
On a fully public index there is nothing to test, and the login stores the token as
stored, not validated.On an index that uses username/password (HTTP basic) authentication, the bearer login is refused and the message routes you to the
SYSAND_CRED_<X>_BASIC_USER/SYSAND_CRED_<X>_BASIC_PASSvariables, the only basic-auth path.
There is no flag to skip validation. An offline or
unreachable index cannot exercise the token, so the login degrades
gracefully to stored, not validated with warnings, and the token is
not transmitted. Responses that could falsely refuse a good token (rate
limiting, redirects) are never verdicts. For the residual case of an
index that genuinely misbehaves, the SYSAND_CRED_* environment
variables remain the way to configure a credential without logging in.
The index’s discovery configuration is fetched before the probes, to learn where the index root and API root live and to scope the credential. The fetch follows these steps:
It starts without credentials.
If the index answers with any 4xx status except 429, including 404, it is retried once with the just-entered token, because a fully private index may hide its discovery document behind authentication (some hosts answer 404, not 401, when credentials are missing). An authenticated 404 is taken as a definitive “no discovery document”.
If discovery is unreachable or gives no clear answer, the credential is scoped to the pattern derived from the URL alone, with a warning.
So each login may send one authenticated request to the discovery URL,
and a fully private index still gets its discovery configuration honored
and can come out as validated (read, api).
Credential scope#
A stored credential covers URL patterns derived automatically from the index
URL: the URL you logged in to (so the discovery fetch itself is
authenticated), plus the index root and API root advertised by the
index’s discovery configuration when they live elsewhere. Each pattern is
the literal URL prefix followed by **, so the credential is only ever
sent within those prefixes. sysand auth login prints the covered
patterns (the Covers line), and sysand auth status lists them per
entry.
For a
URL template target (an index
URL containing {path} or {path_raw}), the pattern is anchored at the
template’s literal prefix before the placeholder, cut back to its last
/. A template whose placeholder sits directly after the host is scoped
to the host root.
The patterns are a login-time snapshot. Reads and publishes re-fetch the
index’s discovery configuration on every run, but a stored credential’s
patterns are not updated to follow it: if the index operator later moves
the index root or API root outside the credential’s patterns, the credential
simply stops matching and requests fail cleanly instead of following the
change. Run sysand auth login again to re-derive the patterns and
re-validate. See the trust model below.
Trust model#
The discovery configuration at the index URL you supply is the trust
anchor: sysand sends the stored credential to the index root and API
root that document advertises, including roots on a different host.
Trusting the index URL means trusting what it points at.
Over plain http, a network attacker at login time can rewrite the
discovery response to point at a hostile API root, which both captures
the freshly entered token and persists that scope until you log in again.
http remains supported for localhost and trusted networks; on untrusted
networks, log in over https only.
Two boundary details: a broad SYSAND_CRED_* pattern takes precedence
over stored credentials and is under your own control, and a server may
redirect a request within the same host, in which case the credential
follows the redirect (cross-host redirects strip it).
Expiry#
When a validating login learns the token’s expiry from the index API, it
stores the timestamp. sysand auth status then shows expires in N days or expired per entry, and sysand publish fails fast before
uploading when the selected stored token is clearly past its expiry.
When a request that used a stored credential fails and the record’s
expiry has passed, sysand warns that the credential may be expired or
revoked and suggests re-authenticating. The server’s rejection remains the
real authority; sysand never refuses a request solely on a locally
stored timestamp except in the publish fast-fail above.
Credential environment variables#
HTTP credentials can also be configured through environment variables
whose names share one arbitrary label <X>. This is the way to provide
credentials in CI, on hosts without a keyring, and for indexes that use
HTTP basic authentication.
Basic authentication:
SYSAND_CRED_<X>=<PATTERN>
SYSAND_CRED_<X>_BASIC_USER=<USER>
SYSAND_CRED_<X>_BASIC_PASS=<PASSWORD>
Bearer token authentication:
SYSAND_CRED_<X>=<PATTERN>
SYSAND_CRED_<X>_BEARER_TOKEN=<TOKEN>
<PATTERN> is a URL glob pattern. <X> is only a label that connects the URL
pattern to its credential values, but it is required and cannot be one of
the role words BASIC_USER, BASIC_PASS, or BEARER_TOKEN.
sysand refuses to run rather than guess when the variables do not form a
complete group:
a variable with no label, such as
SYSAND_CRED_BEARER_TOKEN, because nothing says which credential it belongs to;a pattern with no credential, or a credential with no pattern;
a basic-auth user without its password, or the reverse.
sysand auth login, logout, and whoami are exempt and keep working, so
a malformed variable never locks you out of fixing your credentials.
sysand auth status applies the same validation as every other command and
reports the problem.
URL glob matching#
Sysand uses URL glob patterns with literal path separators:
Pattern syntax |
Meaning |
|---|---|
|
Matches any single character except |
|
Matches any sequence of characters that does not contain |
|
Matches any sequence of characters, including |
For example, the pattern https://*.example.com/** matches URLs such as:
https://www.example.com/projects/project.kpar
https://projects.example.com/index.json
https://projects.example.com/projects/myproject/versions.json
The patterns a stored credential covers use the same syntax and matching; they
are always a literal URL prefix followed by **.
Request behavior#
For normal HTTP(S) fetches, Sysand first makes an unauthenticated request. If the response status is in the 4xx range (except 429, which is never treated as an authentication verdict), Sysand retries with credentials whose pattern matches the URL: environment credentials first, and only if no environment credential matches (or the matching ones also fail) the stored credentials. This is also the point where the keyring is read, at most once per command.
If multiple credential patterns within a source match one URL, Sysand logs a warning and tries the matching credentials until one returns a response outside the 4xx range. If none succeeds, the first response is returned.
Redirects are handled differently:
If credentials were sent to the original URL and that URL redirects to another URL on the same host, the credentials are also sent to the redirected URL.
Sysand does not first try the redirected URL without credentials.
Neither SYSAND_CRED_* variables nor stored credentials configure Git
credentials. For Git projects, Sysand runs Git with the user’s existing
Git setup, such as SSH keys or credential helpers.
Publish credentials#
In the default auto mode, sysand publish first uses trusted publishing when
it detects a supported CI environment. If trusted publishing is unavailable, it
falls back to bearer token credentials that match the upload URL. Basic-auth
entries are ignored for publishing: the upload is bearer-only and the
credential is sent proactively (an upload cannot be retried).
The bearer is selected with source precedence: environment bearer tokens are checked first, and only when none of them matches the upload URL are the stored credentials consulted. Within one source, exactly one pattern must match the upload URL; if more than one does, publishing fails and asks you to refine the patterns of that source.
Outside supported trusted-publishing environments, publishing fails before
the upload request is made if no bearer token matches the upload URL, with
a hint to run sysand auth login <index-url> to store one.
When an upload is rejected, the error names where the selected bearer came
from (a SYSAND_CRED_* variable or a stored credential), because the right
fix differs: rotate or unset the environment variable, or re-run
sysand auth login. A permission failure (HTTP 403) additionally points
at sysand auth status, which shows who the stored token authenticates
as.