# Admin console (/en/docs/guides/admin)

What each page of the admin console is for, how administrators and regular users differ, quotas and upload rate limits, the full lifecycle of upload tokens, and sign-in, sessions and interface preferences.

## Signing in [#signing-in]

The admin console lives at the site root, for example `https://img.example.com/`.

On first start the built-in administrator signs in as `admin` with the factory default password `imgyoung123`. After that sign-in the console goes straight to **Set the admin password**.
The server enforces this: a session still using the factory password can only change the password, sign out and read the current account; every other console API (including console uploads, issuing tokens and managing channels) returns 403 with `须先修改出厂默认密码` (the factory default password must be changed first), whether the request comes from the console or a script.
The default password is public, so do this before the instance is reachable from the internet. If you forget the password, see [Password reset](../operations/password-reset.mdx).

There is no environment variable for setting the initial administrator password, and no bootstrap token provided through an environment variable: a one-off seed like that stops having any effect after its first use, yet stays in the deployment files and leaves people guessing where a value came from. On a headless deployment, sign in once, change the password, and issue a token in the console.

## A tour of the pages [#a-tour-of-the-pages]

The sidebar has three groups, and regular users see only part of it:

| Group | Pages | Visible to |
|---|---|---|
| Content | Dashboard, Images, Upload tokens | Everyone |
| Site | Storage channels, Users, Duplicates, Cleanup, Audit log | Administrators |
| Other | Guide | Everyone |
| Other | Settings | Administrators |

The top bar has page search (⌘K / Ctrl+K opens the command palette), the language switch and the theme switch. The account menu at the bottom of the sidebar holds **Change password** and **Sign out**.

### Dashboard [#dashboard]

Administrators see the whole site:

- Stat cards: Images, Storage used (original bytes in total, replicas not double counted), Users, Accesses (with an estimated traffic figure), Pending cleanup, Possible duplicates.
- **Storage channels**: the health and stored bytes of each channel.
- **Alerts**: alerts that are still open; each resolves itself once its condition no longer holds. **History** leads to the alert history in Settings. Alert kinds are channel unhealthy, replication failed, channel over quota, channel drain failed and backup failed.
- **Top images**: the 10 images with the most accesses.
- When no healthy channel lives off this host's disk, a **Your data only lives on this host** warning appears at the top: image replicas and metadata snapshots exist only on this host, so losing the host loses everything. The condition is that no channel is healthy, not draining and using a driver other than local; it also shows when there is no healthy channel at all. Adding an object storage or cloud drive channel clears it; if you already have one, check on the **Storage channels** page why it is unhealthy.

Regular users see their own image count, space used and quota. While the site has no images yet, the dashboard shows a three-step quick start.

How accesses and the traffic estimate are counted is described in [Accessing images](./access.mdx).

### Images [#images]

A grid of images: administrators see the whole site (**All images**), regular users only their own (**My images**).

- Search by short ID or file name.
- **Filter** by **Upload token**, **Uploaded** time and **Exempt only**; administrators can also filter by **Owner**.
- **Select** lets you pick images and **Delete selected**, at most 100 at a time.
- **Upload**: uploads straight from the console, see [Uploading](./upload.mdx#uploading-in-the-admin-console).

Clicking an image opens its details:

- **Share link**: the full link, with **Copy as** URL, Markdown, HTML or BBCode; below it the available resize widths, each copying a link with `?w=`. When a public site address is set, links use its domain.
- Metadata: format, dimensions, size, owner, source token, references, upload time, last access, accesses, expiry (with **Change expiry**), cleanup exemption and SHA-256.
- **Replicas**: the replica state of this image on each channel (Pending / Uploading / Ready / Failed), attempt count and last error.
- **Delete**; administrators also get **Audit log**, which opens the audit entries targeting this image.

Lifetimes and cleanup exemptions are explained in [Lifecycle and deduplication](./lifecycle.mdx).

### Upload tokens [#upload-tokens]

Issue and manage the credentials for the data API; see [Upload tokens](#upload-tokens) below.

### Storage channels [#storage-channels]

Create, edit, check and delete channels, set their priority, encryption and 302 direct links, and watch each channel's replication progress. Form fields and per-backend configuration are covered in [Storage channels](../channels/index.mdx); a few points matter when working in the console:

- Cloud drive templates have two fields above the authorization control, **App client ID** and **App client secret**, which are stored encrypted with the channel configuration. One-click authorization requires the client ID first; when pasting a token that was authorized with your own app, fill both in too, because the token can only be refreshed by the app that issued it.
- When editing a channel, credential-type parameters are masked: besides passwords, secrets and tokens, this includes access key IDs, WebDAV usernames, client IDs and the like. Leave them unchanged to keep the stored values.
- A channel that fails to load (its configuration cannot be decrypted, the driver cannot be built and so on) shows as **Unhealthy** in the list, with a reason starting with `装载失败:` (load failed). Every health check round retries loading it, and **Probe again** retries loading first as well; it becomes healthy and serves reads and writes again only once it loads and passes the probe.
- With **Encrypt new channels by default** turned on in Settings, a new channel whose **Server-side encryption** stays at **Inherit the global default** is encrypted, so **Allow 302 direct links** is unavailable; to use direct links, set server-side encryption to **Force off**.
- If the provider refreshes the authorization token during **Test connection**, the result is followed by "The provider refreshed the authorization token during the test. The form now holds the new token and will save it; the old one may no longer work." and the authorization field shows **Authorized**; just save. When editing, a stored token that is reused and gets refreshed is written straight back to the channel.
- **Export rclone config** in a channel's row menu (not offered for draining channels) downloads the channel's rclone config file, `imgyoung-channel-<channel ID>.rclone.conf`. With it, a stock rclone can read and write the channel's data without ImgYoung; an encrypted channel also gets a crypt remote, through which reads come back as plaintext.
  The file contains the channel's credentials and OAuth tokens (plus the decryption passwords for an encrypted channel), so keep it offline and safe like the master key; every download is recorded in the audit log. The file's contents and usage are covered in [Channel encryption](../channels/encryption.mdx) and [Storage channels](../channels/index.mdx).

When you delete a channel, **Also delete what this service stored on the channel** is ticked by default and the confirm button reads **Delete and purge**: image replicas and metadata snapshots (and any leftover deep-check files) are deleted one by one in the background, the channel shows as **Draining** meanwhile, and it is removed once they are gone. Files this service did not write are left alone.
Untick it and the button becomes **Remove only**: the channel is removed immediately, and the image replicas and metadata snapshots on it are left as they are for you to deal with at the provider if needed.

If **Delete and purge** fails to delete objects:

- The reason is written into the channel's check result on the very first failure, visible in the channel list, and a "Channel drain failed" alert opens. A purge run that merely times out or is interrupted by a shutdown does not count as a failure.
- Retries then continue automatically with backoff and never stop, so a channel that was only temporarily unavailable finishes draining on its own once it recovers.
- If the credentials are revoked or the bucket no longer exists and retrying cannot succeed, use **Remove now** on the channel: it is removed immediately and any remote objects not yet deleted are left as they are. For a channel you already know cannot be purged, choose **Remove only** when deleting it in the first place.

### Users [#users]

Lists every account with its image count, usage / quota and default TTL. You can create (**New user**), **Edit**, **Reset password** and **Delete**; see [Roles and permissions](#roles-and-permissions) and [Quotas and upload limits](#quotas-and-upload-limits) below.

### Duplicates and Cleanup [#duplicates-and-cleanup]

Two review queues: **Duplicates** compares pairs of images with close perceptual hashes side by side so you can merge or dismiss them; **Cleanup** lists cold images nobody has accessed for a long time so you can keep or delete them. See [Lifecycle and deduplication](./lifecycle.mdx).

### Audit log [#audit-log]

An append-only record of administrative actions, filterable by **Action**, **Actor** (user ID), **Target** (image short ID, channel ID, token ID and so on, exact match) and **Time range**. Recorded actions:

| Object | Actions |
|---|---|
| Channels | Create channel, Edit channel, Delete channel, Export channel rclone config |
| Tokens | Issue token, Revoke token, Rotate token, Delete token |
| Users | Create user, Update user (including password changes and resets), Delete user |
| Images | Update image (lifetime, cleanup exemption), Delete image (including S3 DeleteObject) |
| Reviews | Merge duplicates, Dismiss duplicates |
| Settings | Update settings, Add referer allowlist entry, Remove referer allowlist entry |
| Data | Export data, Create backup, Restore backup |

Uploads themselves and automatic expiry are not audited. Retention is set by **Audit log retention** (`audit_retention_days`); 0, the default, keeps entries forever.

### Guide [#guide]

A built-in quick reference whose curl, PicGo and S3 client examples are filled in with the current site's address. Visible to every user.

### Settings [#settings]

Administrators only, with four tabs:

- **Runtime settings**: every runtime setting, grouped into access & links, upload limits, lifecycle, on-demand processing, deduplication, reliability & channels, CDN cache purge, and alerts & audit. Click **Save changes** and they apply immediately without a restart.
  The **Bootstrap configuration** card below shows the environment variables read-only (listen address, data directory, trusted proxy networks, networks allowed on `/metrics`), the server version and the built-in login rate limit. See [Configuration](../configuration.mdx).
  When a network variable is not set, its default networks are shown; when it is explicitly set to an empty string, **Trusted proxy networks** shows "Empty: forwarded headers are never trusted" and **Networks allowed on /metrics** shows "Empty: open to no one".
- **Hotlink protection**: the Referer allowlist and the **Allow empty referer** switch; see [Accessing images](./access.mdx#hotlink-protection).
- **Backups & export**: metadata snapshots are written to every healthy channel on the **Backup interval**, each channel keeping its own most recent copies. The snapshot table lists each snapshot with its size, time and **Stored on** (the channels holding it, in priority order), with **Back up now**, **Download** and **Restore**; **Export JSON** exports all metadata.
  If some channels fail during **Back up now**, an error such as `备份失败:快照 imgyoung-….db 有 k/n 个渠道未写成: …` lists them, and the channels that succeeded keep the snapshot; with no healthy channel the error is `没有健康渠道可存放快照`. **Download** and **Restore** read the snapshot from the healthy channels holding it in priority order, moving on to the next one when a read fails. See [Backup and restore](../operations/backup.mdx).
- **Alert history**: every alert's open and resolve record. For the alert webhook, see [Monitoring](../operations/monitoring.mdx).

## Roles and permissions [#roles-and-permissions]

Accounts are created only by administrators on the **Users** page; there is no sign-up. There are two roles:

| Capability | Administrator | User |
|---|---|---|
| Upload (token, console, S3) | Yes | Yes |
| View and delete images | Whole site | Own only |
| Change image lifetimes | Whole site | Own only |
| Set cleanup exemptions | Yes | No |
| Issue and rotate own tokens, view own tokens' S3 settings | Yes | Yes |
| View, revoke and delete other users' tokens | Yes | No |
| Storage channels, Users, Duplicates, Cleanup, Audit log, Settings | Yes | No |

Regular users cannot see each other's images or tokens; opening someone else's image details returns 404 `图片不存在` (image not found). Image links themselves are public: anyone with a link can load it.

There can be several administrators. The built-in administrator (user ID `default`) is protected: it cannot be disabled, demoted or deleted, though it can be renamed. No administrator can delete their own account.

On the **Users** page:

- **New user**: username, initial password (at least 8 characters; usable right away), role, quota and default TTL.
- **Edit**: change the username, role, quota or default TTL, or tick **Disabled**. Disabling ends all of the user's sessions at once, and their tokens can no longer upload.
- **Reset password**: sets a new password and ends all of the user's sessions immediately.
- **Delete**: refused while the user still owns images, so delete those first; the user's tokens and sessions go with the account, and audit entries stay.

## Quotas and upload limits [#quotas-and-upload-limits]

- **Quota**: set per user when creating or editing the account, in MB or GB; empty or 0 means unlimited. Every reference counts in full: each image counts the complete size of its physical file, and deduplication merges do not reduce usage. There is only this one way of counting, so nobody can lower their usage by uploading the same images as someone else. An upload that would exceed the quota gets 403.
  Users see their space used and quota on the dashboard; administrators see the **Usage / quota** column on the **Users** page.
- **Upload rate limit**: `upload_rate_per_min` under **Settings → Runtime settings**, applied to every user alike; 0 (the default) turns it off. Uploads by the same user through tokens, the console and S3 share one allowance.
- **Concurrent uploads**: `max_concurrent_uploads`, shared by the whole site, 8 out of the box.

What happens when a limit is hit, and the status codes, are covered in [Uploading](./upload.mdx#formats-and-limits).

## Upload tokens [#upload-tokens-1]

Upload tokens are the credentials for the data API (`/api/upload`, `/api/upload/url`) and the S3 protocol. They can only upload: they cannot sign in to the console or call the console API.

There are exactly two kinds of credentials: a password plus session for the console, and upload tokens for the data API and S3 (S3 signatures use the same tokens). Tokens cannot call the console API so that a leaked token can do no more than upload as its user; the console API is additionally protected against CSRF. A token's permission is also limited to uploading: issuing one per client, each revocable on its own, is enough to contain a leak.

### Issuing [#issuing]

On the **Upload tokens** page, click **Issue token**:

- **Purpose**: for example `picgo` or `blog-script`, at most 64 bytes. A user cannot have two active tokens with the same name; if the old token of that name has expired, issuing a new one revokes the expired record automatically.
- **Expiry**: Never, 7, 30, 90 or 365 days. An expired token stops working exactly as if it were revoked.

After issuing, a dialog shows the token secret (`imgy_` followed by 43 characters) **this one time only**; once closed it can never be shown again. The database keeps only its SHA-256 and a 12-character identifying prefix; a token is a high-entropy random string, so a plain hash is enough and no slow password hash is needed.
Use it as the request header `Authorization: Bearer <token>`.

### List and status [#list-and-status]

The list shows name, prefix, status (Active / Expired / Revoked), expiry, last used and issue time. **Last used** is written in batches about once a minute.
Every image records the token it was uploaded with, and the **Images** page can filter by **Upload token**.

### Row actions [#row-actions]

Open **More actions** at the end of a row:

| Action | Applies to | Effect |
|---|---|---|
| **S3 client settings** | Your own active tokens | Shows the S3 access key, secret and endpoint for this token; you can view them as often as you like. See [Upload clients](./clients.mdx) |
| **Rotate** | Your own active tokens | Atomically issues a new token with the same name and revokes the old one. The new secret is also shown only once; the old token and its S3 credential stop working immediately. A token that never expired still never expires; one with an expiry gets the same duration again, counted from now |
| **Revoke** | Active tokens | Stops working immediately: clients using it can no longer upload, and its S3 credential stops working too. The record stays |
| **Delete record** | Revoked or expired tokens | Removes it from the list; audit entries stay. Active tokens cannot be deleted; revoke them first |

### Administrators viewing other users' tokens [#administrators-viewing-other-users-tokens]

Administrators can use **View by user** to switch to any user's token list and **Revoke** or **Delete record** there.
**Rotate** and **S3 client settings** are only available for your own tokens: both hand out working credentials, so using them on someone else's token would amount to taking over that user's upload access.

## Sessions and security [#sessions-and-security]

| Mechanism | How it works |
|---|---|
| Password storage | argon2id (64 MiB memory, 3 iterations, 1 lane) with a random salt per password. Passwords need at least 8 characters |
| Login rate limit | Counted separately per client IP and per username: 5 attempts in a row each, then one more every 12 seconds; beyond that the response is 429. A successful sign-in resets the count. Behind a trusted reverse proxy, the client IP comes from `X-Forwarded-For` |
| Username enumeration | An unknown username and a wrong password return the same error and take about the same time to check |
| Sessions | Signing in issues a 256-bit random session ID in the cookie `imgyoung_session`, marked `HttpOnly` and `SameSite=Lax`, plus `Secure` when the client reaches the site over HTTPS. The server stores only its hash, so a leaked database cannot be used to hijack sessions. Sessions last a fixed 30 days and are not extended by use |
| CSRF | Each session is bound to a CSRF token that every state-changing request must send in the `X-CSRF-Token` header; the console does this automatically |
| Factory password | While the built-in administrator still uses the factory default password, the server only allows changing the password, signing out and reading the current account; every other console API returns 403 |
| Changing passwords | **Change password** asks for the current password; afterwards every device of that user is signed out and must sign in again |
| Disabling and resets | Disabling a user or resetting their password ends all of their sessions at once; a disabled user cannot sign in and all of their tokens stop working |
| Page hardening | Every response carries `X-Frame-Options: DENY` and `X-Content-Type-Options: nosniff`; console pages also carry a Content Security Policy that allows scripts and API calls only from this site and forbids embedding in other pages |

Both the HTTPS check and the client IP depend on forwarding headers from your reverse proxy, which are trusted only from the networks in `IMGYOUNG_TRUSTED_PROXIES`. See [Reverse proxy](../operations/reverse-proxy.mdx).

## Language and theme [#language-and-theme]

- Language: the language button in the top bar switches between 简体中文 and English. On a first visit the browser language decides (Chinese browsers get Simplified Chinese, everything else English); after that your choice is remembered for a year.
- Theme: the theme button in the top bar offers Light, Dark and System. Dark is the default, and the choice is remembered as well.

Both preferences are stored only in the current browser and do not follow your account.
