Documentation
¶
Overview ¶
Package policyexceptions keeps the local policy exceptions of one DataTug project: time-boxed allow rules that the policy author adds to their own local policy, on this machine, for one role, one table of one source and an exact list of fields. An agent proposes one; the person applies, rejects or revokes it in their own terminal. An exception is never an approval that is valid anywhere else, and the policy files themselves are never changed: an applied, unexpired exception is overlaid on the policy document it names each time policies are loaded, and only while that document is byte for byte the one the exception was bound to.
The store lives at <project>/.datatug/policy-exceptions/: one JSON file per exception and an append-only audit.jsonl, which records names and scopes and never a row value.
Local Perimeter keeps an honest agent inside the lines. A real security boundary needs DB-native permissions or DataTug Cloud approvals.
Index ¶
- Constants
- Variables
- func Applicable(data []byte, codec access.Codec) error
- func CheckDuration(d time.Duration) error
- func CheckName(what, name string) error
- func Fingerprint(data []byte) string
- func NewID() (string, error)
- func Overlay(data []byte, codec access.Codec, binding Binding, exceptions []Exception, ...) ([]byte, []string, error)
- func OverlayProjected(original, data []byte, codec access.Codec, binding Binding, ...) ([]byte, []string, error)
- func Scope(e Exception, fields []string, expires time.Time) string
- func SourceFingerprint(sourceURL string) string
- func Subset(sub, set []string) bool
- func ValidID(id string) bool
- type Binding
- type Event
- type Exception
- func (e Exception) Active(now time.Time) bool
- func (e Exception) Duration() time.Duration
- func (e Exception) IsSession() bool
- func (e Exception) LifetimeLine(until *time.Time) string
- func (e Exception) ProposedBy() string
- func (e Exception) RuleID() string
- func (e Exception) Status(now time.Time) State
- type PolicyRef
- type SessionBinding
- type State
- type Store
- func (s Store) Audit() ([]Event, error)
- func (s Store) Commit(id string, expected []byte, change func(*Exception) (Event, error)) error
- func (s Store) EndSession(binding SessionBinding, now time.Time) ([]string, error)
- func (s Store) Get(id string) (Exception, []byte, error)
- func (s Store) List() (exceptions []Exception, unreadable []string, err error)
- func (s Store) Propose(e Exception) error
- func (s Store) Record(event Event) error
- func (s Store) RecordExpiries(exceptions []Exception, now time.Time) error
Constants ¶
const ( DefaultDuration = 15 * time.Minute MaxDuration = 8 * time.Hour // PendingTTL is how long a proposal waits for the person. PendingTTL = 30 * time.Minute // RulePrefix starts the id of the rule and rule set an exception adds. RulePrefix = "exception-" )
Limits of an exception.
const ( // LifetimeSession limits an exception to the harness session that // proposed it (and its deadline): only a reader reporting the same // harness and session gets it. The session is self-reported, so this // narrows who can use an exception; it does not authenticate anyone. LifetimeSession = "session" // LifetimeDeadline limits an exception to its deadline only. LifetimeDeadline = "deadline" )
Lifetimes of an exception.
const AuditFile = "audit.jsonl"
AuditFile is the audit log's file name inside the store.
const HonestLimit = "" /* 275-byte string literal not displayed */
HonestLimit is the sentence every exception screen carries.
const Kind = "local-exception"
Kind is the kind every exception and audit event carries.
Variables ¶
var DirName = filepath.Join(".datatug", "policy-exceptions")
DirName is the store's folder, relative to the project directory.
var ErrChanged = errors.New("the policy exception changed while it was being decided; nothing was recorded")
ErrChanged is the failure of a commit whose exception changed on disk since it was read: the decision is not recorded.
var ErrNotApplicable = errors.New("the policy binds no roles to rule sets, so an exception cannot name a role in it")
ErrNotApplicable marks a policy document an exception cannot be added to: it binds no roles to rule sets.
var ErrNotFound = errors.New("policy exception not found")
ErrNotFound is the failure of a lookup of an exception the store does not hold.
Functions ¶
func Applicable ¶
Applicable reports whether a document can carry exceptions: it is a principal policy set (rule sets bound to roles).
func CheckDuration ¶
CheckDuration refuses a duration outside (0, MaxDuration].
func CheckName ¶
CheckName refuses a name that is not one literal identifier: a pattern (*, ?, [], {}), a path separator, white space or a control character could widen an exception beyond the one table or field it names.
func Fingerprint ¶
Fingerprint is the hex sha256 of a policy document's bytes.
func Overlay ¶
func Overlay(data []byte, codec access.Codec, binding Binding, exceptions []Exception, now time.Time) ([]byte, []string, error)
Overlay adds to the policy document data one allow rule for each exception that is active at now, matches binding in full, and was bound to exactly these bytes. Each rule is its own rule set, named like the rule, bound to the exception's role: operations [query], path /<table>, fields as applied, and nothing else. It returns data itself and no ids when nothing applies, else the re-encoded document and the ids of the exceptions it added.
func OverlayProjected ¶ added in v0.152.0
func OverlayProjected(original, data []byte, codec access.Codec, binding Binding, exceptions []Exception, now time.Time) ([]byte, []string, error)
OverlayProjected is Overlay for a document whose rules select environments or instances: exceptions bind to original, the bytes on disk, and their rules are added to data, original as it stands for the read (policyscope.Project). When nothing applies it returns data itself.
func Scope ¶
Scope renders the exact scope screen apply shows before the person confirms. expires is the expiry the decision records, fixed before the screen is shown.
func SourceFingerprint ¶
SourceFingerprint is the identity of a resolved source: the hex sha256 of the source reference the project resolves its id to (a SQLite or inGitDB path, or the environment variable of a server source).
Types ¶
type Binding ¶
type Binding struct {
ProjectID, Env, Source, SourceIdentity, PolicyPath string
// Reader is the self-reported harness session of the reading process;
// a session exception applies only to exactly the session it is bound to.
Reader SessionBinding
}
Binding is what a read must match for an exception to apply: the project, its environment, the source id and identity, and the project-relative path of the policy document being loaded.
type Event ¶
type Event struct {
At time.Time `json:"at"`
Kind string `json:"kind"`
Event string `json:"event"` // proposed, applied, narrowed, rejected, revoked, expired, used
ID string `json:"id"`
ProjectID string `json:"projectId"`
Env string `json:"env"`
Source string `json:"source"`
Policy string `json:"policy"`
Role string `json:"role"`
Table string `json:"table"`
Fields []string `json:"fields,omitempty"`
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
Reason string `json:"reason,omitempty"`
// Lifetime and BoundSession are the exception's: which harness session,
// if any, the permission was given to.
Lifetime string `json:"lifetime,omitempty"`
BoundSession *SessionBinding `json:"session,omitempty"`
// Caller is the self-reported harness of the process that wrote the
// event (Store.Caller): attribution only. On a used event it is the
// session that read under the exception.
Caller *callercontext.Caller `json:"caller,omitempty"`
}
Event is one audit record. It names scopes and never holds a row value.
type Exception ¶
type Exception struct {
ID string `json:"id"`
Kind string `json:"kind"`
ProjectID string `json:"projectId"`
Env string `json:"env"`
Source string `json:"source"`
// SourceIdentity is SourceFingerprint of the source the exception was
// proposed for, as the project resolved it: the same source id pointed at
// another database is another source.
SourceIdentity string `json:"sourceIdentity"`
Policy PolicyRef `json:"policy"`
Role string `json:"role"`
Table string `json:"table"`
Fields []string `json:"fields"`
AppliedFields []string `json:"appliedFields,omitempty"`
Reason string `json:"reason"`
DurationSeconds int64 `json:"durationSeconds"`
State State `json:"state"`
ProposedAt time.Time `json:"proposedAt"`
PendingExpiresAt time.Time `json:"pendingExpiresAt"`
DecidedAt *time.Time `json:"decidedAt,omitempty"`
ExpiresAt *time.Time `json:"expiresAt,omitempty"`
DecisionReason string `json:"decisionReason,omitempty"`
ExpiryRecorded bool `json:"expiryRecorded,omitempty"`
// Caller is the self-reported harness the proposal came from:
// attribution only, never authority.
Caller *callercontext.Caller `json:"caller,omitempty"`
// Lifetime is session or deadline; an exception stored before lifetimes
// existed is a deadline one.
Lifetime string `json:"lifetime,omitempty"`
// BoundSession is the harness session a session exception applies to.
BoundSession *SessionBinding `json:"session,omitempty"`
}
Exception is one stored local policy exception.
func (Exception) LifetimeLine ¶
LifetimeLine is the one line the apply and reject screens show about how long the exception lasts; until is the deadline when it is known.
func (Exception) ProposedBy ¶
ProposedBy is the one line the apply and reject screens show about who proposed the exception.
type PolicyRef ¶
type PolicyRef struct {
Name string `json:"name"`
Path string `json:"path"` // relative to the project directory, slash-separated
SHA256 string `json:"sha256"`
}
PolicyRef binds an exception to one policy document of the project.
type SessionBinding ¶
SessionBinding is the harness session a session exception is bound to.
func (SessionBinding) Short ¶
func (s SessionBinding) Short() string
Short is the binding as screens show it: "claude-code session 0f88afc6…".
type Store ¶
type Store struct {
ProjectDir string
// Caller is stamped on every event this store writes that names none.
Caller *callercontext.Caller
}
Store is the exception store of one project directory.
func (Store) Commit ¶
Commit changes one exception under the store's lock, only if its file still holds exactly expected. change may refuse by returning an error. The event change returns is appended to the audit log first and the new state is written only after it: a decision that cannot be audited is never recorded, and a write that fails after the event leaves the old state in force.
func (Store) EndSession ¶
EndSession marks every pending or applied session exception bound to exactly this harness session as ended, with one ended event each, under the store's lock, and returns their ids. It only ever narrows access.
func (Store) List ¶
List reads every exception, sorted by proposal time, and names the files it could not read: an unreadable file contributes nothing, and the others still count. Only a store folder that cannot be listed is an error.
func (Store) RecordExpiries ¶
RecordExpiries marks every exception that expired at now and was not yet recorded, writing one expired event each, in one pass under the store's lock, and returns the first failure. It is best effort: it never changes what is applied, because expiry is evaluated against the clock on every load.