The key-application service

Note

Part of the upcoming 0.2 line. The service sources are in services/access/ on the main branch. The procedure below describes the intended deployment; the service’s README and its example configuration list the exact setting names and are authoritative where they differ.

The key-application service receives applications for model keys from the website, lets the kLLM team review them in the administration console, and delivers the key files of the granted catalog models to approved applicants (Model keys). It is a small Python application (FastAPI served by uvicorn) with a SQLite database. It is separate from the kllm package and is not needed to use kLLM. In production it is served under https://kllm.agentnetwork.org.cn/api, with the administration console at https://kllm.agentnetwork.org.cn/admin/ and the application form at https://kllm.agentnetwork.org.cn/apply.

Interfaces

Endpoint

Purpose

GET /api/catalog

List the catalog models that applicants may request, for the application form.

POST /api/applications

Submit an application: name, email address, organization, role, country, intended use, the catalog models applied for and agreement to the terms. A hidden field rejects automated submissions, sizes are limited and submissions are rate-limited per client address and per email address.

GET /api/verify?token=...

Confirm the email address of an application through the one-time link sent to it.

GET /api/health

Report that the service is running, for health checks.

GET and POST /api/deliveries/{token}

The one-time delivery page of an approved applicant, and the download of the archive of granted model keys from it.

Administration endpoints

List, filter and view applications; approve with a grant of catalog models, reject or request more information; add notes; issue, reissue or revoke key delivery; view the audit log; export CSV. They require an administrator session and are used through the administration console.

Security properties

  • Administrator sessions use a cookie marked HttpOnly, Secure and SameSite=Strict, and every state-changing request carries a CSRF token header. Passwords are hashed with scrypt; a second factor (TOTP) can be enabled per account.

  • Key delivery. Model key files are read from a directory configured on the server, one file per catalog model, when a delivery link is issued (to check that they exist) and when it is used. They are never stored in the database and never sent by email. An approved applicant receives an email with a one-time, expiring download link to an archive that holds the key files of the granted models.

  • Audit. Every administrator action is recorded in an audit log.

  • Origins. Cross-origin requests are accepted only from the configured website origin, and responses carry strict security headers.

  • Retention. Rejected and stale applications are deleted or anonymized after a configured period.

The service holds personal data. The website’s privacy page describes what the form collects and how long it is kept; keep it consistent with the retention setting.

Configuration

The service is configured through environment variables, normally kept in an .env file next to the Compose file. services/access/.env.example lists every variable with its default; services/access/README.md describes them in full. Variables ending in _FILE read a secret from a file and take precedence over the plain variable. The main settings:

Variable

Purpose

ACCESS_SECRET_KEY

Required, at least 32 characters. Keys the hashes of client addresses and email addresses.

ACCESS_PUBLIC_BASE_URL

The external HTTPS address under which the reverse proxy exposes the service’s /api routes, without the /api segment, used in verification and download links. Production: https://kllm.agentnetwork.org.cn.

ACCESS_SITE_URL, ACCESS_VERIFY_REDIRECT_PATH

The address of the public website, whose origin is the only origin allowed to call the public form endpoints, and the page that verification links redirect to (default /apply).

ACCESS_ADMIN_ORIGINS, ACCESS_ADMIN_URL

The origins of the administration console (default: the origin of ACCESS_PUBLIC_BASE_URL), and an optional link to it for reviewer notifications.

ACCESS_DATABASE

The SQLite database file. Place it on a persistent volume.

ACCESS_TRUSTED_PROXIES

The addresses whose forwarding headers are trusted, that is, the reverse proxy.

ACCESS_KEY_DIR

The directory of model key files, one per catalog model, named after the catalog model: Qwen3-8B-kllm.key for Qwen3-8B-kllm. A grant that names a model without a readable key file is refused. The files are never copied into the database, logged or emailed.

ACCESS_KEY_PASSPHRASE

Optional. The passphrase of the issued key files, shown on the delivery page and never in email. When it is unset, the delivery page states that the kLLM team provides the passphrase separately.

ACCESS_CATALOG_FILE, ACCESS_CATALOG_MODELS

The catalog models that applicants may request: a catalog file in the format of tools/catalog/models.json, whose held entries are left out, or a comma-separated list of catalog names. Without either, the service offers its built-in copy of the catalog, which leaves out the held MiniMax H3 entry.

ACCESS_MAIL_BACKEND, ACCESS_SMTP_*, ACCESS_MAIL_FROM, ACCESS_MAIL_REPLY_TO, ACCESS_NOTIFY_EMAILS

The mail backend (smtp, console for development only, or disabled), the SMTP host, port, security mode (starttls, ssl or none; tls is accepted as a synonym of ssl) and credentials, the sender and reply-to addresses, and the reviewers notified of new applications. The port defaults to 465 for ssl, 587 for starttls and 25 for none. See Outgoing mail.

ACCESS_VERIFICATION_TTL_HOURS, ACCESS_DELIVERY_TTL_HOURS

How long a verification link and a key download link stay valid (defaults 48 and 72 hours).

ACCESS_COOKIE_SECURE, ACCESS_REQUIRE_TOTP, ACCESS_SESSION_TTL_HOURS, ACCESS_SESSION_IDLE_MINUTES

Administrator sessions: the Secure cookie flag (required except on localhost), a mandatory second factor, and the session lifetime and idle timeout.

ACCESS_RATE_*, ACCESS_LOGIN_MAX_FAILURES, ACCESS_LOGIN_LOCKOUT_MINUTES

Rate limits per client address and per email address, and the sign-in lockout.

ACCESS_RETENTION_MODE, ACCESS_RETENTION_*_DAYS, ACCESS_RETENTION_INTERVAL_HOURS

Whether expired records are anonymized or deleted, the retention periods per record state (defaults: unverified 14 days, rejected 90, inactive with information requested 180, approved 730, audit entries 730) and how often retention runs.

Outgoing mail

Production mail is sent from hi@anet0.com, a Tencent Exmail mailbox. Exmail accepts authenticated SMTP with implicit TLS on port 465:

ACCESS_MAIL_BACKEND=smtp
ACCESS_SMTP_HOST=smtp.exmail.qq.com
ACCESS_SMTP_SECURITY=ssl
ACCESS_SMTP_PORT=465
ACCESS_SMTP_USERNAME=hi@anet0.com
ACCESS_SMTP_PASSWORD_FILE=/run/secrets/smtp_password
ACCESS_MAIL_FROM=kLLM <hi@anet0.com>
ACCESS_MAIL_REPLY_TO=hi@anet0.com
  • Credentials. The user name is the full mailbox address. Keep the password out of .env and the repository: put it in a file readable by the service only and name the file in ACCESS_SMTP_PASSWORD_FILE. Where the mailbox requires a client-specific password for SMTP clients, generate one in the Exmail web console and use it here instead of the sign-in password.

  • Sender. Exmail sends only for the authenticated mailbox, so the address in ACCESS_MAIL_FROM must be hi@anet0.com. Information requests are answered by email; ACCESS_MAIL_REPLY_TO directs replies to the monitored mailbox.

  • Check. python -m access check validates the configuration, and python -m access mail-test --to ADDRESS sends a test message with the mail settings. During development, ACCESS_MAIL_BACKEND=console writes messages, including their links, to the log instead; never use it in production.

Deployment

The service ships with a Dockerfile and a Compose file. A typical deployment runs one container behind a TLS-terminating reverse proxy on a host operated by the kLLM team.

  1. Prepare the host. Create a directory for the database on persistent storage, and a key directory that only the service’s container can read. Issue the key file of each catalog model into it with tools/catalog/issue_key.py on the machine that holds the master key (Issuing model keys); the master key itself never goes to this host.

  2. Configure. Copy services/access/.env.example to .env, set the variables listed above, and keep the file readable by the operator only. It contains the SMTP credentials.

  3. Start the service. From services/access:

    $ docker compose up -d --build
    

    The service listens on a local port for the reverse proxy; do not publish that port to other hosts.

  4. Configure the reverse proxy. Terminate TLS for the service’s public address and forward requests to the local port. Make sure the service sees the client address, because per-address rate limits otherwise apply to the proxy’s own address. Serve the administration console from the same site (The kLLM website). Every route of the service is under /api, so a single proxy location that passes the path through unchanged covers it; Production deployment gives the production layout.

  5. Check health.

    $ curl -s https://kllm.agentnetwork.org.cn/api/health
    
  6. Point the website at the service. When the website is served from the same origin as the service, as in production, build it with VITE_ACCESS_API unset; otherwise set it to the service’s public address (The kLLM website).

Issuing model keys

The key of each catalog model is derived from the catalog’s master key, which the kLLM team keeps offline (Keys). On the machine that holds the master key, write the key file of each catalog model with a passphrase for the issued files, then copy the files to the service’s key directory:

$ python tools/catalog/issue_key.py Qwen3-8B-kllm --master master.key -o keys/Qwen3-8B-kllm.key

issue_key.py reads the master key’s passphrase from KLLM_PASSPHRASE or the terminal, and the passphrase of the new file from KLLM_NEW_PASSPHRASE or the terminal. Only the derived model key goes into the file. Use the same passphrase for every issued file when it is shown on the delivery page through ACCESS_KEY_PASSPHRASE. The service reads the public fingerprint of each file without the passphrase. python -m access keys and the console’s settings summary list, for every catalog model, whether its key file is present and its fingerprint:

$ docker compose exec access python -m access keys

Administrator accounts

Administrator accounts are created on the host, not through the web interface:

$ docker compose exec access python -m access admin create --email reviewer@example.org --totp

The command asks for the password and, with --totp, enrols a second factor interactively. python -m access admin --help lists the other account actions (list, set-password, disable, enable, totp-enable, totp-disable). Create one account per person, so that the audit log identifies who acted, and enable the second factor for every account.

Reviewing applications

  1. A new application appears in the console’s queue once its email address is confirmed.

  2. Review the stated intended use and the catalog models applied for. Approve with a grant of one or more catalog models, reject, or request more information; add notes for other reviewers. A grant can be narrower than the request, and it can be changed later; every change is recorded in the audit log.

  3. For an approved application, issue key delivery. The applicant receives an email with the one-time download link. The delivery page explains what a model key is, a key shared by every applicant approved for that model that is not a confidentiality boundary, and offers one archive with the key file of each granted model and instructions (kllm key import qwen3-8b-kllm Qwen3-8B-kllm.key, then kllm run with the model).

  4. The console shows whether the link has been used or has expired. Issue a new link if needed, or revoke an unused one.

The console also shows the number of applications per day and the approval rate.

Operations

  • Backups. Back up the SQLite database file regularly while the service is idle, or with the sqlite3 .backup command. The key directory is not part of the backup; the key files can be issued again from the master key at any time.

  • Updates. Rebuild and restart the container with docker compose up -d --build.

  • Logs. The container logs record requests and errors. They are not the audit log, which is kept in the database and shown in the console.

  • New catalog models. When a catalog model is added, issue its key file into the key directory and add the model to ACCESS_CATALOG_FILE or ACCESS_CATALOG_MODELS so that applicants can request it.

  • Key rotation. When the key version of a catalog model is raised, issue the new key file into the key directory under the same name. Future deliveries carry the new key; copies of the model downloaded earlier still need the earlier key.

  • Upgrades from the community key. ACCESS_KEY_FILE and ACCESS_KEY_FILENAME are no longer accepted; the service refuses to start while either is set. Replace them with ACCESS_KEY_DIR.

See also