Concepts
Scopes
A scope defines what a credential permits: a set of (resource, verb) pairs. Both fields are opaque byte strings. QHermes performs no path parsing, no glob expansion, no hierarchy interpretation. Two values are either exactly equal or they are not.
Permissions
from qhermes.kernel import make_policy
policy = make_policy(
resources=[b"/transfers/check", b"/transfers/approve"],
actions=[b"POST"],
hours_valid=1,
) Resource and verb strings are application-defined. They can be HTTP paths, file paths, topic names, operation identifiers, or any other byte sequence meaningful to the application. QHermes does not interpret them.
For asymmetric permissions (different actions on different resources), supply explicit pairs:
policy = make_policy(
permissions=[
(b"/source", b"GET"),
(b"/artifacts", b"PUT"),
],
hours_valid=2,
) Wildcards
The only special value is b"*". In either field it matches any single value during scope subset checks. A resource string such as b"src/**" is a fixed identifier, not a glob. Wildcard pattern interpretation belongs in the application layer.
# Grant access to any resource with verb GET
policy = make_policy(
resources=[b"*"],
actions=[b"GET"],
hours_valid=1,
) Subset enforcement
When verifying a delegation chain, QHermes checks that every permission in a child credential is covered by the parent. A parent permission covers a child when resource and verb match exactly, or when the parent field is b"*".
If the parent holds (b"*", b"GET"), the child may specify any resource with verb GET. If the parent holds (b"/data", b"GET"), the child cannot add (b"/admin", b"GET"); verification will raise.
Time bounds
All time parameters to make_policy are keyword-only. Timestamps are Unix epoch seconds throughout; passing milliseconds produces incorrect caveat evaluation without error.
# Absolute window
policy = make_policy(
permissions=[(b"/deploy/prod", b"POST")],
minutes_valid=15, # 15-minute credential
)
# Or with explicit timestamps
import time
policy = make_policy(
permissions=[(b"/deploy/prod", b"POST")],
not_after=int(time.time()) + 900,
) The child's validity window must fall within the parent's. A child cannot extend its own expiry beyond what the parent granted.
Next: Python API reference