237 lines
11 KiB
Markdown
237 lines
11 KiB
Markdown
# Deployment-Managed Cache Permissions
|
|
|
|
Status: proposed
|
|
|
|
## Summary
|
|
|
|
Stop treating cache access permissions as application policy. Notarius should
|
|
create chunk-plan and checkpoint state through conventional Unix filesystem
|
|
semantics and leave the effective owner, group, and access policy to the
|
|
operator's provisioned directories, process umask, setgid inheritance, and
|
|
default ACLs.
|
|
|
|
Notarius continues to own safe paths, symlink rejection, atomic publication,
|
|
cache validation, and recovery. It does not decide whether a configured cache
|
|
is private to one account, shared by a trusted group, or readable under some
|
|
other deployment policy.
|
|
|
|
## Motivation And Current Problem
|
|
|
|
The current cache implementations enforce a policy that was stronger than the
|
|
application requires:
|
|
|
|
- the chunk-plan store creates and repeatedly converges cache directories to
|
|
`0700` and atomic files to `0600`; and
|
|
- the checkpoint recorder supplies `0700` directory and `0600` file modes to
|
|
the shared confined writer, which explicitly applies the file mode during
|
|
atomic publication.
|
|
|
|
This prevents an administrator from using ordinary Unix provisioning to share
|
|
one cache between a service account and trusted operator accounts. Adding a
|
|
second Notarius-specific `private` or `group` configuration policy would solve
|
|
that immediate symptom, but it would preserve the underlying ownership error:
|
|
Notarius would still decide access policy instead of respecting the deployment.
|
|
|
|
Routine Notarius operation does not require an application-enforced private
|
|
cache. Cache records can contain source-derived data, so operators must be told
|
|
what they are storing, but sensitivity does not imply that Notarius should
|
|
override a deliberately configured filesystem policy. API credentials remain
|
|
outside cache payloads and continue to follow their existing configuration and
|
|
redaction boundaries.
|
|
|
|
## Target Policy
|
|
|
|
The target contract is:
|
|
|
|
> Notarius creates cache entries with conventional maximum creation modes and
|
|
> allows the operating system to determine effective access through the
|
|
> process umask, inherited group ownership, and default ACLs.
|
|
|
|
Concretely:
|
|
|
|
- newly created cache directories request `0777`;
|
|
- newly created cache files, including atomic temporary files, request `0666`;
|
|
- the kernel applies the invoking process's umask and any applicable default
|
|
ACL;
|
|
- setgid directories provisioned by the operator control inherited group
|
|
ownership and, on supported Unix systems, setgid propagation to descendants;
|
|
- Notarius does not call `chmod`, `chown`, or `chgrp` to impose or repair a
|
|
cache access policy; and
|
|
- Notarius does not reject a cache merely because its effective modes are more
|
|
or less permissive than a Notarius-selected default.
|
|
|
|
This is policy inheritance, not literal copying of a parent directory's mode
|
|
bits. Directories and non-executable data files appropriately have different
|
|
maximum modes. A parent mode alone also does not determine child modes: an
|
|
operator who needs a particular result must configure the service umask and,
|
|
where appropriate, a setgid parent or default ACL.
|
|
|
|
For example, a service started with umask `0077` ordinarily creates `0700`
|
|
directories and `0600` files. A trusted-group deployment can provision a
|
|
setgid cache root and use umask `0007`, or an equivalent default ACL, to obtain
|
|
`0770` directories and `0660` files. These are deployment choices rather than
|
|
configuration values interpreted by Notarius.
|
|
|
|
## Public Contract
|
|
|
|
No new Notarius configuration field, environment variable, or CLI flag is
|
|
introduced. In particular, do not add the previously considered
|
|
`cache.access: private|group` setting.
|
|
|
|
Existing cache enablement, locations, chunk-plan modes, resume controls,
|
|
identity, and compatibility rules remain unchanged. The behavioral change is
|
|
that effective permissions on newly created cache entries are controlled by
|
|
the deployment environment rather than forcibly converged by Notarius.
|
|
|
|
The policy applies uniformly to both cache families:
|
|
|
|
- canonical chunk plans; and
|
|
- invocation checkpoints.
|
|
|
|
Output and debug storage are outside this feature's scope. Their current
|
|
contracts must not change incidentally through a shared helper refactor. A
|
|
separate review may later consider whether the same deployment-owned policy is
|
|
appropriate for those surfaces.
|
|
|
|
## Filesystem And Atomic-Publication Requirements
|
|
|
|
Permission deference must cover every cache write path, not only final files:
|
|
|
|
- configured cache-family roots created by Notarius;
|
|
- chunk-plan source-digest directories;
|
|
- checkpoint identity and stage directories;
|
|
- atomic temporary files; and
|
|
- final files published by rename.
|
|
|
|
Atomic publication must not bypass the deployment policy. Go's
|
|
`os.CreateTemp` creates a file with `0600`, and applying `chmod(0666)` afterward
|
|
would bypass the process umask. Cache writers therefore need an atomic
|
|
temporary-file creation path that chooses a collision-resistant name and opens
|
|
it with `O_CREATE|O_EXCL` and requested mode `0666`, allowing the kernel to
|
|
apply umask and default ACL policy at creation time.
|
|
|
|
Replacement writes publish a newly created temporary inode. Its effective
|
|
permissions and ACL are therefore those selected for new entries by the
|
|
current parent and process policy; Notarius does not copy mode bits or ACLs
|
|
from the replaced file. Operators who require stable replacement permissions
|
|
must express that policy through a stable service umask and directory default
|
|
ACL.
|
|
|
|
Opening an existing cache for writing must not alter its root or descendant
|
|
modes. Read-only lookup of a missing cache must remain side-effect free.
|
|
|
|
## Preserved Safety And Correctness Boundaries
|
|
|
|
Deferring access policy does not weaken Notarius's filesystem correctness
|
|
rules. The implementation must preserve:
|
|
|
|
- confined resolution beneath the selected cache root;
|
|
- rejection of symlinked or unexpected cache entries;
|
|
- validation of source digests, checkpoint identities, schemas, and recorded
|
|
provenance;
|
|
- atomic replacement, existing synchronization behavior, cleanup of abandoned
|
|
temporary files, and complete-record visibility;
|
|
- existing missing, malformed, incompatible, and operational-error recovery
|
|
classifications; and
|
|
- separation of output, cache, and debug roots.
|
|
|
|
An administrator's decision to permit multiple accounts to reach one cache
|
|
does not create a new Notarius guarantee for simultaneous writers. Preserve
|
|
the existing atomicity and validation behavior, but do not add locking or claim
|
|
transactional coordination across concurrent processes as part of this work.
|
|
Concurrent writes to the same multi-file checkpoint identity remain subject to
|
|
the currently documented cache behavior; the implementation documentation
|
|
must state any existing limitation accurately.
|
|
|
|
## Architectural Boundaries
|
|
|
|
The target removes application-owned cache modes without creating a general
|
|
permission-policy framework. Chunk-plan and checkpoint storage may use
|
|
different filesystem APIs internally, but both must implement the same
|
|
deployment-owned result. Pipeline modules and the runner remain unaware of
|
|
physical roots and filesystem access policy.
|
|
|
|
Any shared writer API must distinguish an application-enforced exact mode from
|
|
an umask-filtered maximum creation mode. The cache change must not silently
|
|
alter output or debug behavior merely because checkpoints currently share
|
|
filesystem helpers with those surfaces.
|
|
|
|
## Existing Cache Migration
|
|
|
|
Stopping permission convergence does not change existing inode modes. A cache
|
|
tree created by an older Notarius version may therefore remain `0700`/`0600`
|
|
and inaccessible to another account after upgrade.
|
|
|
|
Operators who want a different policy must either:
|
|
|
|
- remove the reconstructible cache and allow it to be recreated beneath a
|
|
correctly provisioned parent and service environment; or
|
|
- stop writers and deliberately update ownership, modes, and ACLs using normal
|
|
system-administration tools.
|
|
|
|
Notarius must not perform a recursive startup migration. The same rule applies
|
|
when an operator wants to make a previously shared cache private: change the
|
|
deployment policy and deliberately migrate or recreate existing state when an
|
|
immediate whole-tree change is required.
|
|
|
|
## Documentation And Decision Records
|
|
|
|
The final documentation set includes an accepted ADR titled along the lines of
|
|
"Defer cache access policy to the deployment." The ADR records the durable
|
|
ownership boundary so later maintenance does not reintroduce
|
|
application-enforced modes solely because cache data can contain source-derived
|
|
content.
|
|
|
|
The new ADR need not supersede ADR-0005 or ADR-0007: those decisions govern
|
|
canonical chunk-plan reuse and checkpoint recording/reuse, while leaving exact
|
|
storage permissions to operational contracts. It should reference those ADRs
|
|
and the output/cache/debug separation established by ADR-0006 and refined by
|
|
ADR-0007.
|
|
|
|
In the final current-behavior documentation, `docs/operations.md` owns cache
|
|
provisioning, umask, setgid, default ACL, migration, sensitivity, and
|
|
concurrent-use guidance. Architecture owns the deployment/application boundary;
|
|
the configuration reference clarifies that selecting a cache path does not
|
|
select its permissions; and the internal state guide explains the implemented
|
|
write mechanics. The release that first contains the feature summarizes the
|
|
operational change without becoming its canonical reference.
|
|
|
|
Current-behavior documentation must not describe this target behavior before
|
|
the implementation lands.
|
|
|
|
## Verification Expectations
|
|
|
|
Behavioral tests protect kernel-selected permissions for both cache families,
|
|
non-mutation of existing roots, atomic replacement, and representative
|
|
assembled CLI wiring. Existing confinement, symlink, validation, identity,
|
|
reuse, and recovery coverage remains authoritative. Umask tests are POSIX-only,
|
|
serialized, and restore process-global state. The default suite does not create
|
|
privileged accounts or groups; setgid and default-ACL examples may be verified
|
|
in a purpose-built Linux deployment environment.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- No cache access-mode field or application-specific private/group policy is
|
|
added.
|
|
- Both chunk-plan and checkpoint writers request `0777` for new directories
|
|
and `0666` for new files, including atomic temporary files.
|
|
- Effective permissions reflect the process umask and applicable default ACLs
|
|
rather than a later Notarius chmod.
|
|
- Existing cache roots and descendants are not chmod-converged during reads or
|
|
writes.
|
|
- A private deployment can obtain private cache state through its filesystem
|
|
provisioning and umask without a Notarius-specific setting.
|
|
- A trusted-group deployment can obtain shared cache state through setgid,
|
|
umask, and/or default ACL policy without a Notarius-specific setting.
|
|
- Existing cache trees are not recursively migrated.
|
|
- Chunk-plan and checkpoint enablement, lookup, recording, resume, identity,
|
|
validation, confinement, atomicity, and recovery semantics remain unchanged.
|
|
- Output and debug permission behavior does not change as an incidental effect.
|
|
- Notarius performs no user/group management, ownership mutation, ACL
|
|
management, privilege escalation, or cross-process locking.
|
|
- The ADR and canonical operations, architecture, configuration, and internal
|
|
documentation accurately describe the implemented boundary; the next
|
|
release note summarizes the change when that release is prepared.
|
|
- Focused tests and repository-wide `go test ./...`, `go vet ./...`, and
|
|
`go build ./cmd/notarius` checks pass.
|