Skip to main content
Version: v5.6.1

7.2 Users, groups, rules, policies

Included from Basic tier

After this you can: create a user, watch them hit the wall, grant read-only access with a rule, and widen it deliberately through policies, assignments and groups — one action at a time.

Before you start — tick each one:

  • Lesson 7.1 — Denied means not found; the denial event (opschain learn 7.1)

In the GUI: Users & Groups in the Operations area creates the user and the group; Manage security under Governance holds the rules, policies and assignments.

OpsChain decides access with a small path-based model: a rule is a node path plus four grantable actions (readable, updatable, deletable, executable), a policy is a named bundle of rules, and an assignment binds a policy to a username, a group name, or everyone. A principal's authority is the union of the rules on every policy assigned to it — directly or through its groups. This lesson walks the loop in the order the Academy's least-privilege exercise checks it: the user, then the refusal, then the rule. You need a superuser login (lesson 0.1); the identity and authorisation verbs ride that login (updatable on /admin is the determination — a superuser holds it by construction).

1. Create a user​

DANA_PASSWORD=… opschain admin users create dana --password-env DANA_PASSWORD
Created dana (provider local)

The password never travels on the command line: --password-env names a variable, --password-prompt reads without echo, and with neither the server generates one and returns it exactly once. (A server running the hardened security posture refuses local users that are not break-glass — workforce identities belong to the directory there; this lesson assumes the evaluation posture of the server you installed in lesson 0.1.)

Directory users need no create step at all: they appear at their first sign-in through their connection's login leg (opschain login --provider <code>), or ahead of it via the connection's sweep (opschain admin identity providers sweep <code>) — and either way can be grouped and assigned before they ever sign in (lesson 7.5).

2. Hit the wall​

Log in as the new user and look for the project:

opschain login --server https://opschain.example.com --username dana
opschain whoami
Server: https://opschain.example.com
Identity: dana
Provider: local
Superuser: false
opschain nodes show demoenterprise
Error: the server answered HTTP 404: The requested resource could not be found

No assignment, no authority: every node answers not found, exactly as lesson 7.1 promised, and opschain nodes list is an empty estate. Nothing is misconfigured — dana simply has not been granted anything yet.

The refusal is already on the record. Back as the superuser:

opschain login --username admin
opschain events --type-search authz --since 10m
WHEN ID TYPE NODE USER CORRELATION DETAIL
2026-10-15T04:14:02Z 019a0d7e… warn:authz:denied - - - NodeService/GetNode refused: readable on /projects/demoenterprise (principal 019a0d7e-…, session)

The row names the refused procedure and the /projects/demoenterprise path. Remember the throttle: one event per refused procedure per five minutes, estate-wide — if you repeat the refused lookup inside the window, no second row appears, and if someone else was refused the same lookup moments before you, yours is the one that was swallowed. Wait out the window and try again.

3. Grant read-only access with a rule​

Still as the superuser, create the narrowest useful rule: the project, readable, nothing else:

opschain admin authz rules create --path /projects/demoenterprise \
--readable --name "demoenterprise read-only"
created authorisation rule /projects/demoenterprise (0199c2…)

A rule grants only what it names — every action defaults to denied, so a rule created with no action flags grants nothing. Now carry it to dana through a policy and an assignment:

# demo-readers.yaml
name: demo-readers
description: Read the demo estate
opschain admin authz policies create -f demo-readers.yaml
opschain admin authz policies attach-rule demo-readers --rule 0199c2…
opschain admin authz policies assign demo-readers --user dana
replaced the assignments of "demo-readers": now 1 assignment(s)
assignments:
KIND ASSIGNEE ID
user dana 0199c4…

assign replaces the whole assignment set — read it first with policies assignments when editing an existing policy. A policy's rules can also be declared inline in the create/update YAML; attach-rule is the incremental lane.

Now as dana again: opschain nodes list shows the demoenterprise subtree and nodes show demoenterprise answers — but creating a change still answers not found:

opschain runs create change demoenterprise/environments/dev/assets/web --action deploy
Error: the server answered HTTP 404: The requested resource could not be found

That is the exercise's end state: a user who can see the project and not change it, with the refusal on the record.

4. Widen it one action at a time​

The four actions gate different verbs. Watch each wall fall by adding one flag at a time on a path:

  • --readable — visibility: lists include the subtree, shows answer. Without it everything else is moot — the lookup answers not found before any other check runs.
  • --executable — running work: creating changes and workflow runs against the subtree. Readable without executable sees the estate but runs create answers not found.
  • --updatable — writing: creating and editing nodes, properties, settings, triggers, schedules. Read-and-run operators without it cannot alter what they operate.
  • --deletable — the delete lifecycle (admin nodes delete and friends authorise read plus deletable).
opschain admin authz rules create --path /projects/demoenterprise/environments/dev \
--readable --executable --name "dev read+run"
created authorisation rule /projects/demoenterprise/environments/dev (0199c7…)

Attach it to the policy and dana may run in dev while still only looking at prod. A policy built from narrow rules like this is how "may run in dev, may only look at prod" is actually spelled.

5. Groups: the second lane​

Assigning to people one by one does not scale; assign to a group and manage membership instead. Local groups are created empty; members are added by username, which the CLI resolves to the exact user id (--provider picks one of a same-named pair):

opschain admin groups create app-operators
opschain admin groups add-member app-operators dana
opschain admin authz policies assign demo-readers --user dana --group app-operators
Created local group app-operators (no members yet — 'admin groups add-member app-operators <username>')
replaced the assignments of "demo-readers": now 2 assignment(s)
assignments:
KIND ASSIGNEE ID
user dana 0199c4…
group app-operators 0199c5…

Either assignment alone would have sufficed; the union rules. Read the identity back:

opschain admin users show dana
Username: dana
Id: 019a0d7e-10c2-7f7a-8b53-3b1c9a4f2e07
Provider: local
Break-glass: -
Disabled: -
Created: 2026-10-15T04:12:44Z
Local groups: app-operators
Groups: app-operators

Groups is the effective set — directory-mirror groups plus local memberships — and is what assignments match against. Local group names share one namespace with directory group names, so a policy assigned to a group covers both populations, and adding a local membership in a directory-named group grants that group's policies without touching the directory. Deleting a group is refused by name while policy assignments still reference it.

Two facts for mixed estates: a user assignment is per-identity, so a local account sharing a directory account's name inherits nothing automatically (when a username is held by more than one provider, --user is refused as ambiguous and --user-id picks the exact one); and group-conferred authority does not cross providers either — a mirror that should inherit through a group needs its own local membership. The recommended break-glass fire door is a dedicated local-only account with its own name and its own policy; the admin surfaces specifically need updatable on /admin.

Check yourself​

  • After the read-only rule is assigned, what does the user see for a listing and what for a create — and which event records the refusal?
  • Verified version: opschain academy runs the least-privilege exercise against this lesson — the user, the refusal, then the rule, each later than the one before.

What you learned​

  • Rules name a path and the actions they grant; policies bundle rules; assignments bind policies to users, groups or everyone.
  • A new user is refused everywhere until assigned, and the refusal is already on the record as warn:authz:denied.
  • A read-only rule makes the estate visible and nothing else; every further action is a deliberate flag.
  • Groups are the scalable lane; effective groups are what assignments match, across local and directory populations.
  • assign replaces the set — read it before editing.

Next: lesson 7.3 — Tokens, automation principals and the review export (opschain learn 7.3), or back to your track (opschain learn).