@@ -8,13 +8,13 @@ Every mutating API path writes an audit event. The log is at **Audit**.
|
||||
|
||||
## What an event carries
|
||||
|
||||
| Field | Meaning |
|
||||
| --- | --- |
|
||||
| Field | Meaning |
|
||||
| ------ | -------------------------------------------------------- |
|
||||
| Action | A dotted name, e.g. `server.created`, `settings.updated` |
|
||||
| Actor | Who did it |
|
||||
| Target | The object acted on |
|
||||
| Detail | A short human-readable note |
|
||||
| Time | When |
|
||||
| Actor | Who did it |
|
||||
| Target | The object acted on |
|
||||
| Detail | A short human-readable note |
|
||||
| Time | When |
|
||||
|
||||
## What is recorded
|
||||
|
||||
@@ -36,7 +36,7 @@ lookup.
|
||||
|
||||
## Retention
|
||||
|
||||
Audit events are not swept by the workflow log retention setting — that setting
|
||||
Audit events are not swept by the workflow log retention setting that setting
|
||||
governs run logs only. Audit history stays until the instance does.
|
||||
|
||||
:::warning It is a log, not a control
|
||||
|
||||
@@ -7,7 +7,7 @@ sidebar_label: Browser console
|
||||
An SSH, RDP or VNC session in a browser tab, with no client software and no
|
||||
inbound port on the target beyond the one the protocol already uses.
|
||||
|
||||
Protocol handling is Apache Guacamole's — the control plane proxies a WebSocket
|
||||
Protocol handling is Apache Guacamole's the control plane proxies a WebSocket
|
||||
to a **guacd** daemon and manages credentials around it.
|
||||
|
||||
## Requirements
|
||||
@@ -27,8 +27,8 @@ From a server's page, choose **Console**. Then:
|
||||
1. The UI calls `POST /api/console/connect`, which mints a **one-time** session
|
||||
token.
|
||||
2. The browser opens a WebSocket to `GET /api/console/tunnel` with that token.
|
||||
3. The server marks the token consumed — atomically, so a second use cannot
|
||||
race — and proxies the connection to guacd.
|
||||
3. The server marks the token consumed atomically, so a second use cannot
|
||||
race and proxies the connection to guacd.
|
||||
|
||||
## Credentials
|
||||
|
||||
@@ -52,19 +52,19 @@ is worth one connection at most, and only until it is used.
|
||||
## Session behaviour
|
||||
|
||||
Closing the tab ends the session. There is no reconnect and no session
|
||||
persistence — reopening mints a new token and a new connection.
|
||||
persistence reopening mints a new token and a new connection.
|
||||
|
||||
## Auditing
|
||||
|
||||
Opening a console is an audited action, with actor, server and time. What
|
||||
happens *inside* the session is not recorded: there is no session capture or
|
||||
happens _inside_ the session is not recorded: there is no session capture or
|
||||
keystroke log. If you need that, it has to come from the target machine.
|
||||
|
||||
## When it does not work
|
||||
|
||||
| Symptom | Cause |
|
||||
| --- | --- |
|
||||
| Connects then closes immediately | guacd unreachable — check `GUACD_ADDR` and that the container is up |
|
||||
| SSH refuses the key | The stored key has no private half, or is not in the target's `authorized_keys` |
|
||||
| RDP fails on a fresh credential | Credentials are consumed on open; a retry needs them entered again |
|
||||
| Hangs at connecting | The control plane cannot reach the target on the protocol port |
|
||||
| Symptom | Cause |
|
||||
| -------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| Connects then closes immediately | guacd unreachable check `GUACD_ADDR` and that the container is up |
|
||||
| SSH refuses the key | The stored key has no private half, or is not in the target's `authorized_keys` |
|
||||
| RDP fails on a fresh credential | Credentials are consumed on open; a retry needs them entered again |
|
||||
| Hangs at connecting | The control plane cannot reach the target on the protocol port |
|
||||
|
||||
@@ -9,12 +9,12 @@ from, and a notification path when they stop being satisfied.
|
||||
|
||||
## Types
|
||||
|
||||
| Type | Checks | Options |
|
||||
| --- | --- | --- |
|
||||
| `http` | An HTTP(S) URL | method, expected status, keyword that must appear in the body, allow insecure TLS |
|
||||
| `tcp` | A host and port accept a connection | — |
|
||||
| `icmp` | A host answers ping | — |
|
||||
| `tls` | A certificate is valid and not expiring | warn N days before expiry |
|
||||
| Type | Checks | Options |
|
||||
| ------ | --------------------------------------- | --------------------------------------------------------------------------------- |
|
||||
| `http` | An HTTP(S) URL | method, expected status, keyword that must appear in the body, allow insecure TLS |
|
||||
| `tcp` | A host and port accept a connection | |
|
||||
| `icmp` | A host answers ping | |
|
||||
| `tls` | A certificate is valid and not expiring | warn N days before expiry |
|
||||
|
||||
An `http` monitor with a keyword is usually the one you want for an application:
|
||||
a 200 that returns an error page still fails the keyword.
|
||||
@@ -23,12 +23,12 @@ a 200 that returns an error page still fails the keyword.
|
||||
|
||||
Every monitor has a **runner**:
|
||||
|
||||
| Runner | Meaning |
|
||||
| --- | --- |
|
||||
| `server` | The control plane's scheduler performs the check |
|
||||
| Runner | Meaning |
|
||||
| ----------- | -------------------------------------------------------------- |
|
||||
| `server` | The control plane's scheduler performs the check |
|
||||
| a server ID | That server's agent performs it locally and reports the result |
|
||||
|
||||
Use `server` for anything reachable from the control plane — public endpoints,
|
||||
Use `server` for anything reachable from the control plane public endpoints,
|
||||
your own front door. Use an agent for anything only reachable from inside the
|
||||
target network: a database on a private subnet, a service bound to localhost, a
|
||||
device on a management VLAN.
|
||||
@@ -41,8 +41,8 @@ different questions, and outages usually live in the gap.
|
||||
|
||||
## Interval, retries and state
|
||||
|
||||
- **Interval** — how often to check.
|
||||
- **Retries** — how many consecutive failures are tolerated before the state
|
||||
- **Interval** how often to check.
|
||||
- **Retries** how many consecutive failures are tolerated before the state
|
||||
flips.
|
||||
|
||||
A monitor sits in `pending` until its first result. Failures accumulate; once
|
||||
@@ -65,7 +65,7 @@ hours does not send a message per interval.
|
||||
|
||||
The monitor detail page shows:
|
||||
|
||||
- **Uptime**, from hourly rollup records — checks performed, how many were up,
|
||||
- **Uptime**, from hourly rollup records checks performed, how many were up,
|
||||
and mean latency per hour. Rollups are what make the graph cheap to draw over
|
||||
long windows.
|
||||
- **Incidents**, each with a start, a resolution and the cause recorded at the
|
||||
@@ -74,5 +74,5 @@ The monitor detail page shows:
|
||||
## Disabling versus deleting
|
||||
|
||||
Disabling stops the checks and keeps the history. Deleting removes the monitor.
|
||||
Prefer disabling for anything seasonal — the uptime record is usually the part
|
||||
Prefer disabling for anything seasonal the uptime record is usually the part
|
||||
you wanted.
|
||||
|
||||
@@ -15,9 +15,9 @@ Manage them at **Settings → Notifications**.
|
||||
|
||||
Posts JSON to a URL you choose.
|
||||
|
||||
| Setting | |
|
||||
| --- | --- |
|
||||
| `url` | Required |
|
||||
| Setting | |
|
||||
| ------- | -------- |
|
||||
| `url` | Required |
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -35,31 +35,31 @@ after 10 seconds.
|
||||
|
||||
### Discord
|
||||
|
||||
| Setting | |
|
||||
| --- | --- |
|
||||
| `url` | Discord webhook URL |
|
||||
| Setting | |
|
||||
| ------- | ------------------- |
|
||||
| `url` | Discord webhook URL |
|
||||
|
||||
Posts the alert as message content.
|
||||
|
||||
### Slack
|
||||
|
||||
| Setting | |
|
||||
| --- | --- |
|
||||
| `url` | Slack incoming webhook URL |
|
||||
| Setting | |
|
||||
| ------- | -------------------------- |
|
||||
| `url` | Slack incoming webhook URL |
|
||||
|
||||
### Telegram
|
||||
|
||||
| Setting | |
|
||||
| --- | --- |
|
||||
| `token` | Bot token |
|
||||
| Setting | |
|
||||
| --------- | ----------- |
|
||||
| `token` | Bot token |
|
||||
| `chat_id` | Target chat |
|
||||
|
||||
### SMTP
|
||||
|
||||
| Setting | |
|
||||
| --- | --- |
|
||||
| `host`, `port` | Required |
|
||||
| `from`, `to` | Required |
|
||||
| Setting | |
|
||||
| ---------------------- | ---------------------------------------------------- |
|
||||
| `host`, `port` | Required |
|
||||
| `from`, `to` | Required |
|
||||
| `username`, `password` | Optional; auth is skipped when the username is empty |
|
||||
|
||||
Port `465` uses implicit TLS; anything else uses STARTTLS.
|
||||
@@ -83,7 +83,7 @@ something that needs to branch on status.
|
||||
## Testing
|
||||
|
||||
Every channel has a **Test** button. It dispatches a fabricated down event for a
|
||||
monitor called "Test monitor", through the real delivery path — so a test that
|
||||
monitor called "Test monitor", through the real delivery path so a test that
|
||||
arrives proves the credentials, the network path and the destination, not just
|
||||
the configuration form.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ External Secrets Operator.
|
||||
|
||||
## Groups and values
|
||||
|
||||
A **group** is a named bundle — `prod-db`, `registry`, `acme-api`. Inside it are
|
||||
A **group** is a named bundle `prod-db`, `registry`, `acme-api`. Inside it are
|
||||
key/value pairs.
|
||||
|
||||
Group by consumer, not by type. A group is the unit a workflow step references
|
||||
@@ -22,7 +22,7 @@ a group holding everything is over-sharing to every step that needs any of it.
|
||||
**Secrets → New group**, then add keys.
|
||||
|
||||
Values are write-then-hidden. The list shows keys, never values. **Reveal** is a
|
||||
separate action on a separate endpoint, and it writes an audit event — so
|
||||
separate action on a separate endpoint, and it writes an audit event so
|
||||
looking at a secret is a recorded act.
|
||||
|
||||
Deleting a single key and deleting the whole group are separate operations.
|
||||
@@ -42,7 +42,7 @@ library entry.
|
||||
|
||||
:::warning A step can print its own secrets
|
||||
Injection puts values in the environment. If your script echoes them, or runs
|
||||
with `set -x`, they land in the run log — which is stored on disk and readable
|
||||
with `set -x`, they land in the run log which is stored on disk and readable
|
||||
in the UI. Vantage does not scrub step output.
|
||||
:::
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@ sidebar_label: Servers
|
||||
---
|
||||
|
||||
The fleet. Every managed machine runs an agent that connects outbound to the
|
||||
control plane, and everything else in Vantage — keys, workflows, monitors,
|
||||
consoles — targets these records.
|
||||
control plane, and everything else in Vantage keys, workflows, monitors,
|
||||
consoles targets these records.
|
||||
|
||||
## Enrolling a server
|
||||
|
||||
@@ -16,14 +16,14 @@ a one-liner to run as root on the target machine.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
| Status | Meaning |
|
||||
| --- | --- |
|
||||
| Status | Meaning |
|
||||
| --------- | --------------------------------------------------- |
|
||||
| `pending` | Enrolment created; the agent has not registered yet |
|
||||
| `active` | The agent registered and is syncing |
|
||||
| `offline` | Last-seen passed the threshold |
|
||||
| `active` | The agent registered and is syncing |
|
||||
| `offline` | Last-seen passed the threshold |
|
||||
|
||||
The offline sweep runs every two minutes, so a machine that has just gone away
|
||||
takes a little while to be marked as such. That delay is intentional — a single
|
||||
takes a little while to be marked as such. That delay is intentional a single
|
||||
missed poll is not an outage.
|
||||
|
||||
## The server detail page
|
||||
@@ -37,9 +37,9 @@ Which SSH keys are assigned to this machine, and their state. See
|
||||
|
||||
Agents report:
|
||||
|
||||
| Data | Refreshed |
|
||||
| --- | --- |
|
||||
| CPU, memory, swap, load | every 30 seconds |
|
||||
| Data | Refreshed |
|
||||
| ---------------------------------------- | ---------------- |
|
||||
| CPU, memory, swap, load | every 30 seconds |
|
||||
| Partitions, kernel, full static snapshot | every 15 minutes |
|
||||
|
||||
The two carry separate timestamps, so a stale static snapshot beside fresh
|
||||
@@ -50,15 +50,15 @@ metrics is normal rather than a fault.
|
||||
Agents check for pending package updates hourly and report the count. From the
|
||||
server page you can:
|
||||
|
||||
- **Apply updates** — pushes `ApplyUpdatesCmd` down the command stream. The
|
||||
- **Apply updates** pushes `ApplyUpdatesCmd` down the command stream. The
|
||||
agent runs the platform's package manager and reports back.
|
||||
- **Update agent** — pushes `UpdateAgentCmd` with a target version; the agent
|
||||
- **Update agent** pushes `UpdateAgentCmd` with a target version; the agent
|
||||
downloads the release, verifies it and replaces itself. See
|
||||
[Agent updates](../operations/agent-updates.md).
|
||||
|
||||
:::warning Applying updates is not scheduled or staged
|
||||
It runs now, on that machine. If you need ordering, health gates or a canary,
|
||||
build it as a [workflow](./workflows.md) instead — that is what workflows exist
|
||||
build it as a [workflow](./workflows.md) instead that is what workflows exist
|
||||
for.
|
||||
:::
|
||||
|
||||
@@ -69,7 +69,7 @@ Opens a browser SSH, RDP or VNC session. See [Browser console](./browser-console
|
||||
## Windows servers
|
||||
|
||||
Windows agents register, heartbeat, run workflow steps and report inventory.
|
||||
They do not manage `authorized_keys` — the poll loop stops after the heartbeat
|
||||
They do not manage `authorized_keys` the poll loop stops after the heartbeat
|
||||
on any non-Linux host. This is a deliberate scope decision, not a gap being
|
||||
worked on.
|
||||
|
||||
@@ -94,5 +94,5 @@ no longer running to remove them. Revoke and let the agent apply the change
|
||||
|
||||
Each server has its own token. The control plane stores only its SHA-256; the
|
||||
plaintext exists in the agent's `0600` config and nowhere else. There is no way
|
||||
to read a token back out of the control plane — if one is lost, re-enrol the
|
||||
to read a token back out of the control plane if one is lost, re-enrol the
|
||||
machine.
|
||||
|
||||
@@ -11,7 +11,7 @@ Settings require the `owner` or `admin` role.
|
||||
|
||||
:::info Where instance settings went
|
||||
Members and single sign-on used to live at `/settings/instance`. They are now
|
||||
the Access group at the top of this page — splitting "who can sign in" from "how
|
||||
the Access group at the top of this page splitting "who can sign in" from "how
|
||||
this instance behaves" produced two half-pages and a nav entry nobody could
|
||||
distinguish from Settings. The old path still redirects.
|
||||
:::
|
||||
@@ -22,10 +22,10 @@ distinguish from Settings. The old path still redirects.
|
||||
|
||||
Add, remove and re-role the people who can sign in.
|
||||
|
||||
| Role | Can |
|
||||
| --- | --- |
|
||||
| `owner` | Everything |
|
||||
| `admin` | Everything except owner-only settings |
|
||||
| Role | Can |
|
||||
| -------- | ---------------------------------------------------- |
|
||||
| `owner` | Everything |
|
||||
| `admin` | Everything except owner-only settings |
|
||||
| `member` | Servers, keys, workflows, monitors, secrets, console |
|
||||
|
||||
Local members authenticate with email and a bcrypt-hashed password.
|
||||
@@ -37,7 +37,7 @@ read-only rows with a link to the portal.
|
||||
|
||||
:::warning HQ-managed users cannot be edited locally
|
||||
Changing the role of, or deleting, an `hq`-sourced user is refused with `409`.
|
||||
HQ owns their role, their password and whether they exist at all — a local
|
||||
HQ owns their role, their password and whether they exist at all a local
|
||||
change would be overwritten by the next sync and would leave two writers for one
|
||||
password hash. Manage them from [People and roles](../hq/people-and-roles.md).
|
||||
:::
|
||||
@@ -46,10 +46,10 @@ password hash. Manage them from [People and roles](../hq/people-and-roles.md).
|
||||
|
||||
Configured per organisation:
|
||||
|
||||
| Field | |
|
||||
| --- | --- |
|
||||
| Issuer | Your provider's issuer URL |
|
||||
| Client ID | |
|
||||
| Field | |
|
||||
| ------------- | ---------------------------- |
|
||||
| Issuer | Your provider's issuer URL |
|
||||
| Client ID | |
|
||||
| Client secret | Stored AES-256-GCM encrypted |
|
||||
|
||||
Sign-in then goes `/auth/oidc/start` → your provider → `/auth/oidc/callback`.
|
||||
@@ -60,7 +60,7 @@ misconfigured or unreachable, a local account is the way back in.
|
||||
## Monitoring
|
||||
|
||||
- **Alert defaults** for monitors.
|
||||
- **Notification channels** — their own page. See
|
||||
- **Notification channels** their own page. See
|
||||
[Notification channels](./notification-channels.md).
|
||||
|
||||
## Integrations
|
||||
@@ -69,11 +69,11 @@ misconfigured or unreachable, a local account is the way back in.
|
||||
|
||||
How long run logs are kept.
|
||||
|
||||
| Value | Meaning |
|
||||
| --- | --- |
|
||||
| unset | 30 days |
|
||||
| Value | Meaning |
|
||||
| -------- | -------------- |
|
||||
| unset | 30 days |
|
||||
| a number | that many days |
|
||||
| `0` | forever |
|
||||
| `0` | forever |
|
||||
|
||||
### ESO read token
|
||||
|
||||
@@ -87,7 +87,7 @@ once, stored as a SHA-256 hash, rotatable. See
|
||||
features and expiry.
|
||||
|
||||
On **self-hosted**, paste a licence here. This works even while the current
|
||||
licence is expired — that is the way out of degraded mode.
|
||||
licence is expired that is the way out of degraded mode.
|
||||
|
||||
On **cloud**, there is no paste form. The endpoint answers `409 cloud_managed`,
|
||||
because a cloud licence is written by HQ directly. The page links to the portal
|
||||
@@ -108,6 +108,6 @@ organisation from the slug and rejects a session belonging to a different one.
|
||||
The label it looks for comes from `APP_ROOT_LABEL`.
|
||||
|
||||
:::warning A wrong `APP_ROOT_LABEL` disables the guard
|
||||
It does not fail loudly — it simply stops matching, and the host check stops
|
||||
It does not fail loudly it simply stops matching, and the host check stops
|
||||
protecting anything. If you serve the UI on a custom domain, set it to match.
|
||||
:::
|
||||
|
||||
@@ -28,7 +28,7 @@ library. You may optionally upload the private half too, in which case it is
|
||||
stored **AES-256-GCM encrypted** under `KEY_ENCRYPTION_KEY`.
|
||||
|
||||
The JSON representation of a key exposes only `has_private_key` and
|
||||
`has_passphrase` — never the material. Retrieving a stored private key is its
|
||||
`has_passphrase` never the material. Retrieving a stored private key is its
|
||||
own endpoint and its own audit event.
|
||||
|
||||
:::tip Why store a private key at all
|
||||
@@ -38,7 +38,7 @@ you are not using the console, do not upload private halves.
|
||||
|
||||
## Assigning
|
||||
|
||||
Assign a key to one or more servers. Within one poll interval — 30 seconds — the
|
||||
Assign a key to one or more servers. Within one poll interval 30 seconds the
|
||||
agent picks up the change.
|
||||
|
||||
## Revoking
|
||||
@@ -58,7 +58,7 @@ until it ends. Kill sessions on the machine if that matters.
|
||||
|
||||
Each poll:
|
||||
|
||||
1. `SyncKeys` returns the desired set of public keys for that server.
|
||||
1. The control plane returns the desired set of public keys for that server.
|
||||
2. The agent reads `/root/.ssh/authorized_keys` and computes fingerprints.
|
||||
3. **If the sets match, it writes nothing.** No disk churn on unchanged state,
|
||||
which is most polls.
|
||||
@@ -77,5 +77,5 @@ it in Vantage.
|
||||
## Recovering from a lockout
|
||||
|
||||
If you have removed every key from a machine and cannot get in, you still have
|
||||
the console — provided a private key is stored — or out-of-band access from your
|
||||
the console provided a private key is stored or out-of-band access from your
|
||||
hosting provider. Vantage has no backdoor and does not keep a break-glass key.
|
||||
|
||||
@@ -13,14 +13,14 @@ back live.
|
||||
|
||||
A step has:
|
||||
|
||||
| Field | Meaning |
|
||||
| --- | --- |
|
||||
| `name`, `description` | Library identity |
|
||||
| `interpreter` | `bash` or `powershell` |
|
||||
| `script` | The body |
|
||||
| `declared_inputs` | Named parameters with defaults and descriptions |
|
||||
| `declared_outputs` | Names this step promises to export |
|
||||
| `secret_refs` | Vault entries injected as environment variables |
|
||||
| Field | Meaning |
|
||||
| --------------------- | ----------------------------------------------- |
|
||||
| `name`, `description` | Library identity |
|
||||
| `interpreter` | `bash` or `powershell` |
|
||||
| `script` | The body |
|
||||
| `declared_inputs` | Named parameters with defaults and descriptions |
|
||||
| `declared_outputs` | Names this step promises to export |
|
||||
| `secret_refs` | Vault entries injected as environment variables |
|
||||
|
||||
### Passing values between steps
|
||||
|
||||
@@ -59,7 +59,7 @@ new install is not staring at an empty page.
|
||||
:::warning Default steps are read-only
|
||||
Editing or deleting one is refused with `409`. Seeding rewrites them on every
|
||||
boot, so an edit would silently revert and a delete would come back at the next
|
||||
restart — refusing is the honest answer.
|
||||
restart refusing is the honest answer.
|
||||
|
||||
To customise one, use the per-step **script override** in the workflow designer,
|
||||
which belongs to that workflow and is not touched by seeding. To add to the
|
||||
@@ -67,7 +67,7 @@ shared library permanently, a file has to be committed to the repository and the
|
||||
server image rebuilt.
|
||||
:::
|
||||
|
||||
The UI mirrors this — the step modal opens read-only and Delete is hidden — but
|
||||
The UI mirrors this the step modal opens read-only and Delete is hidden but
|
||||
the API is the boundary; the UI is the courtesy.
|
||||
|
||||
## Building a workflow
|
||||
@@ -80,11 +80,11 @@ the API is the boundary; the UI is the courtesy.
|
||||
|
||||
### Failure behaviour
|
||||
|
||||
| `on_failure` | Effect |
|
||||
| --- | --- |
|
||||
| `stop` | Abort this server's run. Other servers continue |
|
||||
| `continue` | Record the failure, run the next step anyway |
|
||||
| `retry` | Re-run the step up to `max_retries`, then treat it as a failure |
|
||||
| `on_failure` | Effect |
|
||||
| ------------ | --------------------------------------------------------------- |
|
||||
| `stop` | Abort this server's run. Other servers continue |
|
||||
| `continue` | Record the failure, run the next step anyway |
|
||||
| `retry` | Re-run the step up to `max_retries`, then treat it as a failure |
|
||||
|
||||
### Per-step overrides
|
||||
|
||||
@@ -94,9 +94,8 @@ scoped to that workflow.
|
||||
|
||||
## Running
|
||||
|
||||
**Run** snapshots the resolved steps into the run record and dispatches
|
||||
`RunStepCmd` to each target's agent over the command stream — no waiting for the
|
||||
next poll.
|
||||
**Run** snapshots the resolved steps into the run record and dispatches each step
|
||||
to the target's agent over the command stream no waiting for the next poll.
|
||||
|
||||
:::info Runs freeze their steps
|
||||
The snapshot is why editing a step tomorrow never rewrites what happened today.
|
||||
@@ -119,11 +118,11 @@ nothing further is dispatched.
|
||||
Run logs are swept on a schedule set by `workflow_log_retention_days` in
|
||||
Settings:
|
||||
|
||||
| Value | Meaning |
|
||||
| --- | --- |
|
||||
| unset | 30 days |
|
||||
| Value | Meaning |
|
||||
| -------- | -------------- |
|
||||
| unset | 30 days |
|
||||
| a number | that many days |
|
||||
| `0` | keep forever |
|
||||
| `0` | keep forever |
|
||||
|
||||
## Import and export
|
||||
|
||||
|
||||
Reference in New Issue
Block a user