pub struct ClientHint {
pub name: String,
pub description: Option<String>,
pub category: String,
pub category_label: Option<String>,
pub category_icon: Option<String>,
pub category_order: Option<i64>,
pub icon: Option<String>,
pub visible_to: Option<Target>,
pub show_when: Option<ShowWhen>,
pub confirm: Option<ConfirmHint>,
pub unlock: Option<String>,
}Expand description
Manifest sub-section (#291): marks a job as user-invokable
from the Client App and carries how it presents to the end user.
Parallel to InventoryHint / CheckHint / EmitConfig —
the block’s presence is the opt-in (no separate boolean), and its
required fields (name, category) are enforced by serde at
parse time, so a half-filled catalog entry fails
kanade job create instead of rendering a nameless / tab-less row.
The agent maps this 1:1 into the KLP
UserInvokableJob wire shape
that jobs.list returns; the Client App renders one row per job in
the tab named by category.
Fields§
§name: StringEnd-user-facing title for the job row. The operator-internal
Manifest::id slug is rarely what an end user should read, so
this is required (and validated non-empty by
Manifest::validate). Maps to UserInvokableJob::display_name.
description: Option<String>Optional one-line subtitle under name in the Client App.
Distinct from the operator-facing top-level
Manifest::description — this one is written for the end
user. Maps to UserInvokableJob::display_description.
category: StringWhich Client App tab the job lives in — a free-form category
key (#792). The Client App renders one tab per distinct key.
Well-known keys (software_update, troubleshoot, catalog)
carry built-in tab labels/icons; any other key defines a new tab
(style it with category_label / category_icon). Required and
validated non-empty — without it the agent can’t place the job.
Note: the software_update key also drives the agent’s
maintenance / auto-reboot grouping.
category_label: Option<String>Optional display name for the category’s TAB. Set it on (at least
one of) a custom category’s jobs to name the tab; None ⇒ a
built-in default for a well-known key, else the key itself.
category_icon: Option<String>Optional icon for the category’s TAB (lucide name or data: URL).
None ⇒ Client App default for the key.
category_order: Option<i64>Optional sort order for the TAB; lower sorts first. None ⇒
default (well-known keys keep their familiar order; custom keys
sort after, then by label).
icon: Option<String>Optional icon hint for the job ROW — a lucide-react icon name
or a data: URL. None ⇒ the Client App falls back to the
category’s icon. Surfaced verbatim in jobs.list[].icon.
visible_to: Option<Target>Optional visibility scope for the end-user Client App (#816).
None ⇒ visible to every PC (current behavior). When set, only
agents whose pc_id / group membership match the Target list
the job in jobs.list and may run it via KLP jobs.execute.
This gates the END-USER surface ONLY. Operators are unaffected:
POST /api/exec/{job_id} (SPA / kanade exec) is a separate path
that never consults client:, so an operator can still run the
job on any PC regardless of visible_to. Reuses the schedule
Target shape (all / groups / pcs); a present-but-empty
target is rejected by Manifest::validate.
show_when: Option<ShowWhen>Optional dynamic display gate keyed on a health check’s result.
None ⇒ always listed (current behavior). When set, the agent
lists the job in jobs.list ONLY while the named check: slug’s
latest result is one of ShowWhen::is. The canonical use is an
update action that hides itself once the machine is already current:
pair the update job with a check: that reports ok when up to
date and gate on is: [fail].
Evaluated agent-side at jobs.list time against the live
StateSnapshot.checks, which is keyed by check name — so the
detector check: and this job may live in different manifests and
still share one slug. Distinct from visible_to:
that gates BOTH listing and jobs.execute (an authorization
boundary); show_when gates listing ONLY (a UX hint), so it can’t
cause a list/execute race. New field ⇒ #492 wire rule.
confirm: Option<ConfirmHint>Optional confirmation-dialog config for the Client App’s 実行 button.
None ⇒ the historical default: the client shows a modal
confirmation with a built-in 「「{name}」を実行しますか?」 message
before firing the job (a mis-click guard for a possibly heavy /
destructive action). When set, the operator controls it:
- a bare bool —
confirm: falseruns immediately with no prompt;confirm: trueis the same as omitting the block (default message); - a struct —
confirm: { message: "…" }shows the dialog with a custom message (and, redundantly with the scalar,enabled: falseto suppress it).
Gates the END-USER Client App surface only — the operator POST /api/exec path never consults client:, so an operator-driven run
is unaffected. New field ⇒ #492 wire rule (serde(default) +
skip_serializing_if). Deserializes from bool-or-struct via
[de_confirm]; the JSON schema advertises the struct form (the
scalar is author ergonomics, like ShowWhen::is).
unlock: Option<String>Optional unlock scope — the “裏コマンド” display gate. None (the
overwhelming default) ⇒ the job behaves as it always has. Some(scope)
⇒ the job is hidden from jobs.list unless the calling OS user
currently holds an unlock grant for that scope, obtained by typing the
operator’s secret code into the Client App (support.unlock).
The intended use is helpdesk-only actions: a job that has no business
sitting in an end user’s everyday catalog, but which the IT desk can
surface in seconds while walking that user through a problem — without
an operator-side exec, which needs SPA access and a correctly-cased
pc_id.
The scope is a free-form slug (support, admin, …) matched against
the scopes configured in ServerSettings::support_codes, so one
deployment can run a first-line code and a stronger administrator code
side by side, each revealing a different set of jobs. A scope with no
configured code opens for nobody — a typo hides the job rather than
exposing it.
Listing only, like show_when and unlike
visible_to. The agent does NOT re-check
it in jobs.execute, which has two consequences worth being explicit
about:
- Anything the user can see, they can run — no race where the row is visible, the grant lapses, and pressing 実行 fails on a button they were just looking at.
- It is therefore not a security boundary: a standard user who speaks KLP to the agent’s pipe directly and knows the job id can still run it. Approval controls for privileged work live on the operator (SPA) exec path; this hides a button, it does not guard a capability.
The operator paths are unaffected in both directions: POST /api/exec/{job_id} and kanade exec never consult client:, so an
operator can run the job on any PC whether or not anyone unlocked it.
New field ⇒ #492 wire rule (serde(default) + skip_serializing_if).
Trait Implementations§
Source§impl Clone for ClientHint
impl Clone for ClientHint
Source§fn clone(&self) -> ClientHint
fn clone(&self) -> ClientHint
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for ClientHint
impl Debug for ClientHint
Source§impl<'de> Deserialize<'de> for ClientHint
impl<'de> Deserialize<'de> for ClientHint
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl JsonSchema for ClientHint
impl JsonSchema for ClientHint
Source§fn schema_id() -> Cow<'static, str>
fn schema_id() -> Cow<'static, str>
Source§fn json_schema(generator: &mut SchemaGenerator) -> Schema
fn json_schema(generator: &mut SchemaGenerator) -> Schema
Source§fn inline_schema() -> bool
fn inline_schema() -> bool
$ref keyword. Read more