Skip to content

In the UI

HCL appears in two places in the browser: a view in the Rule Designer, and a tab or modal on the screens where an object is administered. Both go through the same encoder, decoder and apply as the command line — a second save path would drift from the first one within a release.

The UI edits one object at a time. The command line is what handles a whole configuration.

Rule Designer

Two ways in, one destination:

  • the HCL button in the toolbar, an icon showing braces around an H;
  • MORE > HCL in the menu.

The button is a toggle. Its tooltip reads HCL View on the way in and Back to the tree on the way out, and it goes to the primary colour while the view is active.

The HCL view replaces the rule tree the way the flowchart does. The rule toolbar is hidden while it is open and restored on every way out — the editor's own Back, the designer's Back, and the toggle. Leaving with unsaved changes asks first.

A rule opens in the HCL view when the last rule was left in it, in this browser page. See Where HCL opens.

Collapse and expand

In the HCL view the toolbar's expand and collapse buttons fold the text rather than the hidden tree, one level per press. Their tooltips change to Expand One Level and Collapse One Level to say so.

Collapse folds the deepest blocks that are still open. The next press folds the level above over them, and so on until the rule block itself is folded. Expand goes the other way: it opens the outermost folds first, and the blocks inside come back still folded, so each press shows one more level.

The levels are the blocks the editor's gutter can fold, braces inside a heredoc's embedded code included. Folds made by hand in the gutter count: a folded block and everything it hides are skipped by Collapse, and Expand opens the outermost fold wherever it is. Folding only changes what is drawn, so it is never an unsaved change.

Show disabled

Next to All attributes, the Rule Designer's HCL view has a Show disabled switch. It is on when the view opens. Turned off, it folds every block that says disabled = true down to a single fold marker at the block's first line. Disabled blocks that follow one another, with nothing but blank lines between them, share one marker.

The blocks are only folded, not removed. The text still holds them, so saving stores them unchanged; taking them out of the text would have deleted them on the next save. Click a marker, or its gutter arrow, to show one block again. Turn the switch back on to show them all.

The switch acts when it is flipped and when a new text arrives (a reload, a save, All attributes, Format), never while typing, so a block does not vanish as it is given disabled = true. Expand leaves hidden blocks folded, and Collapse skips them. The rule list's HCL modal does not have the switch.

From the rule list

Each row's action menu has an HCL entry, which opens the same editor in a modal titled HCL: <rule name>. Dismissing it asks before discarding.

Saving

A save goes straight into the rule editor's own save. Versioning, the base_version conflict check and the event.rule.* events all come along unchanged, and the optimistic-concurrency dialog is the same one the tree editor shows.

The text is formatted before it is sent, so what gets stored is what the CLI would have written and a later cla export produces no spurious diff. If formatting fails, the raw text is sent anyway.

Nothing broken is ever stored. A file with diagnostics is rejected and the stored rule is untouched.

A rule that reads back but does not compile is refused by the rule compiler. Its errors are shown on the steps they came from (HCL321), not at line numbers of the generated code.

What the text cannot say is put back from the operation itself: the flags the designer's palette copies onto a step from its registration — an ELSE's nested, a holder's holds_children, an IF block's if_block, a dashlet's template, the icon. A rule saved from the HCL view compiles, and takes steps dropped into it in the tree, as one saved from the tree does.

Two things a save from the HCL view still does not keep:

  • opids. Every step of a rule saved from the HCL view is given a new one, so its job history and profile start again.
  • ids with no address. A value the text cannot carry — a category, role or status id that points at nothing that can be addressed, often something since deleted — is written as a comment (HCL106) and is gone from the step once saved.

cla rule-check lists every rule either of these, or anything else, would change.

After a save the editor reloads. A save legitimately changes the text — defaults settle, references canonicalise — and showing stale text invites a second save built on a wrong picture.

In the operation dialog

Double-clicking an operation in the tree opens its dialog, and the dialog has an HCL tab after Note. It shows that one step as it would read in the rule file, without its children, which stay in the tree.

The text is built from the dialog, not from the stored node. Whatever has been changed in Config, Options, Root-Cause Analysis or Note and not applied yet is already in it, laid out exactly as the dialog's Apply would store it. Coming back to the tab reads the other tabs again, unless the HCL has edits of its own. The reload button in the editor does the same on demand.

The text only flows one way, from the tabs into HCL. Leaving the HCL tab with edits in it asks first, with the same choices as every other HCL exit: Cancel, Don't save or Apply.

Apply — the dialog's button, or the one in that question — sends the text to /hcl/step/apply. The server checks it, decodes it and lays it over the dialog's values, and the result becomes the node, exactly as a form Apply would. Nothing is stored until the rule itself is saved, so the rule's own Save, versioning and conflict check still apply. A step with errors is not applied and the dialog stays open.

The Options tab is in the text too, as the step's modifiers, so an option taken out of the text is back at its default after Apply. What the text cannot say is kept from the dialog: the node's own identifiers, and the values the operation's registration put on it.

The tab is stricter than the rule file. It holds exactly one step, a nested step is refused rather than dropped, and the operation cannot change — sh may switch between running locally and remotely, but an if stays an if. See HCL311 to HCL313.

HCL on administered objects

Screen Surface Editable
Resource editor an HCL tab inherits the editor's read-only state
Role detail an HCL tab yes
User group grid an HCL row action, modal HCL: <group> yes
User grid an HCL row action, modal HCL: <user> no — read only

The resource HCL tab is declared unconditionally, unlike the Raw Data tab which only appears in debug mode. It is editable exactly when the properties form is.

After a save, the sibling views are re-read: the role screen re-fetches its actions, users and scopes; the resource editor rebuilds its form from fresh data.

Users are view-only, and why

Everything else on the user screen that changes a user has a side effect the record alone does not carry: inviting sends mail, suspending ends sessions, a password change notifies. A declarative overwrite would write the same fields and fire none of it, so someone flipping active = false in HCL would reasonably expect Suspend to have happened, and it would not have.

Roles and groups have no such actions, which is why they are editable here and users are not. cla import is the supported way to move users between installs.

This is a UI decision, not a server guarantee: /hcl/object/save writes a user for anyone holding action.admin.users, because cla import uses the same path. The hard server-side guard is on credentials — HCL204 — and on the second-order spelling — HCL214.

Where HCL opens

Three places remember whether they were last left on HCL: the Rule Designer's HCL view, the operation dialog's HCL tab and the resource editor's HCL tab. Each is remembered separately, and only as HCL or not HCL. Leave a resource on its HCL tab and the next resource opens there. Leave it on any other tab and the next one opens on its usual first tab. Which other tab it was is not kept.

The flag is kept in the browser page, as window.prefs.hcl_tab_in_rule, hcl_tab_in_op and hcl_tab_in_resource, and nowhere else: not in a cookie, not in local storage, not in the user's preferences. Reloading the page forgets it, and another browser never sees it.

  • A rule tab that is already open keeps the view it was left in. Only a rule being opened follows the flag, and bringing an open rule tab forward makes its view, HCL or not, the one remembered.
  • A new resource has no HCL tab until it is saved, so it opens on its form.
  • An operation with no HCL tab (one without an operation key) opens on its usual tab and leaves the flag alone.
  • Opening straight onto the operation dialog's HCL tab leaves Config unbuilt until it is visited. The text then comes from the step as stored, which is also what Apply would keep.

The role, user and group screens always open where they did before.

Editor behaviour

  • Validation runs 500 ms after you stop typing. Validating on every keystroke would be a request per character.
  • Gutter markers are Ace annotations applied straight to the session rather than through component state, because replacing the document to show a marker would move the caret out from under whoever is typing. Server diagnostics are 1-based and are converted to Ace's 0-based rows and columns.
  • The status line shows one problem: <count> - <line>:<col> <message>. Errors take precedence. Warnings show but do not block. With neither, it reads No problems. Only the first error is shown — later ones are usually the same mistake cascading. The server returns all of them; the editor picks one, cut to fit the line. When there is more to read it underlines on hover, and a click opens Problems: every problem whole, each with its line number as an underlined link that moves the cursor to that line — out of any fold it is in — and after them the server's full message, such as the rule compiler's.
  • A dirty dot • appears while there are unsaved changes.
  • An All attributes switch re-encodes with every operation parameter written out, after asking to discard unsaved edits. It works on the rule editor. On the resource, role and group tabs it is inert: the switch sends all_attributes=1 and /hcl/object/encode ignores the parameter. See Resources.
  • A sensitive banner appears when the text contains b64(: Sensitive values are shown base64-encoded, as b64("..."). Leave one out to keep the stored value.
  • An unsaved-changes banner appears on a resource's HCL tab when the text includes edits made in the resource's other tabs that are not saved yet: This includes changes made in the other tabs that are not saved yet. Save here or in the form to keep them. The tab sends the form's current values with the encode, and /hcl/object/encode lays them over the stored object the way the resource form's Save would store them, without writing anything. Coming back to the tab reads the form again, unless the HCL has unsaved edits of its own. Saving from the HCL tab stores everything shown, and the form is rebuilt from what was stored.

Validation runs two passes. Parsing clean is not the same as meaning something, so the decode runs too. A rule file with no rule block in it gets a dedicated HCL310 No rule block found in this file.

Guards the CLI does not have

The editor refuses a save whose address is a different object:

This is generic_server.other, but the editor is open on generic_server.web01

A rename is allowed — the family and CI class must match — but pasting an unrelated object is not. The tab edits this object; pasting a different one in would otherwise quietly write somewhere the reader is not looking.

The editor also always runs with rename history on and missing references fatal. There is no UI equivalent of --lax, --allow-missing, --dry-run, --prune, --secret-key or --internal.

Endpoints

Path Parameters Returns Permission
/hcl/rule/encode id_rule, all_attributes hcl, rule_type, rule_name, version_id action.admin.rules.view
/hcl/rule/validate hcl valid, diagnostics[] action.admin.rules.view
/hcl/rule/save id_rule, hcl, base_version, overwrite version_id, detected_errors action.admin.rules.modify
/hcl/step/encode draft, all_attributes hcl action.admin.rules.view
/hcl/step/validate hcl, key valid, diagnostics[] action.admin.rules.view
/hcl/step/apply hcl, draft attributes, or diagnostics[] action.admin.rules.view
/hcl/fmt hcl hcl, or diagnostics[] action.admin.rules.view
/hcl/object/encode address or mid, or family=role + id hcl, address, sensitive the four object actions
/hcl/object/validate hcl valid, diagnostics[] the four object actions
/hcl/object/save hcl, address address, changed[], action the four object write actions

The object routes carry the union of four permissions — action.admin.ci.view|action.admin.role|action.admin.users|action.admin.user_groups — because Catalyst's ACL attribute is static. The route check is enough to keep out anyone who administers none of them; the handler then checks the action that actually guards the family in hand. Gating the route on the CI action alone would have locked a role administrator out of the role editor.

Per-family actions are in the people page.

The step routes write nothing. /hcl/step/apply answers the node's new attributes to the dialog; the rule is only written by the rule's own save, which is where action.admin.rules.modify is checked. The Perl syntax check (HCL320) still runs only for a caller holding it.

A family with no UI support returns This editor does not handle that kind of object.

/hcl/fmt is gated on the rules action

The Format button on a role, group or resource editor issues a request guarded by action.admin.rules.view. An administrator who can edit roles but cannot view rules will have it refused.

Families the UI does not cover

The HCL surface in the browser is resource, role, user, group and rules. category, dash, board, notification, calendar, schedule and topic have no UI surface at all — see Other object families.