Skip to main content

ClientHint

Struct ClientHint 

Source
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: String

End-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: String

Which 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: false runs immediately with no prompt; confirm: true is 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: false to 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

Source§

fn clone(&self) -> ClientHint

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ClientHint

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for ClientHint

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl JsonSchema for ClientHint

Source§

fn schema_name() -> Cow<'static, str>

The name of the generated JSON Schema. Read more
Source§

fn schema_id() -> Cow<'static, str>

Returns a string that uniquely identifies the schema produced by this type. Read more
Source§

fn json_schema(generator: &mut SchemaGenerator) -> Schema

Generates a JSON Schema for this type. Read more
Source§

fn inline_schema() -> bool

Whether JSON Schemas generated for this type should be included directly in parent schemas, rather than being re-used where possible using the $ref keyword. Read more
Source§

impl Serialize for ClientHint

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more