mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
fix(memory): the S3 store verifies its bucket and never falls back
Maintainer decisions 2026-10-02: "it has the featurs. fallback not desired" and "Admin sets it once; agent verifies". - MinioStore never configures its bucket: configure()/enable_versioning()/ expire() are gone; the client protocol only reads versioning and lifecycle. - verify_bucket() runs when a MinioStore is opened (once per process per store): versioning Enabled, an enabled lifecycle rule expiring <prefix>/runs/ (none expiring the whole store), S3 Select answering a probe, object tags readable. Every missing piece is listed in one MemoryStoreError with the aws-cli admin command or the permission to grant. - No S3 Select fallback: a server without Select is refused at open, and a failing Select on one object raises (naming RustFS's leading-row schema inference when the field is unbound) instead of filtering locally. - MINIO_REGION is passed through when set. - Tests: fakes for versioning off/unreadable, lifecycle missing/disabled/ tag-narrowed/whole-store, Select missing, tags unreadable, and the pass case; the real-server contract uses OUSAST_MEMORY_TEST_BUCKET (else MINIO_BUCKET) and never writes bucket configuration. - ops/ax/README.md: RustFS support, the admin one-time setup, the agent policy. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7b17eacd44
commit
e26fe4b608
@@ -49,6 +49,58 @@ afterwards (`go clean -cache -modcache`); rerunning the full Substrate install o
|
||||
- A Workspace's `files` reach the actor inline in one environment variable: above ~20 KB of content the template
|
||||
fails with "actor template not found" (an 86 KB and a 31 KB excerpt did; 7 KB ran).
|
||||
|
||||
## Memory store on S3 (MinIO or RustFS)
|
||||
|
||||
`OUSAST_MEMORY=minio://<bucket>[/<prefix>]` puts the plane's memory store (`plane/memory.py`) in an
|
||||
S3-compatible bucket. MinIO and RustFS both work; the maintainer's server is RustFS. Endpoint and credentials
|
||||
come from `.env` (`MINIO_ENDPOINT`, `MINIO_ACCESS_KEY`, `MINIO_SECRET_KEY`, `MINIO_SECURE`, optional
|
||||
`MINIO_REGION`, which avoids a GetBucketLocation call) and are never printed.
|
||||
|
||||
**The store never configures its bucket.** An admin sets it up once. Each time a `MinioStore` is opened it
|
||||
checks the setup (`MinioStore.verify_bucket`), using reads plus one small probe object at
|
||||
`<prefix>/_probe/select.jsonl`. If anything is missing, it refuses to start with a `MemoryStoreError` that lists
|
||||
each missing piece and the admin command that fixes it. Nothing is skipped quietly. The store needs:
|
||||
|
||||
- **versioning `Enabled`**, because provenance cites object version ids;
|
||||
- **an enabled lifecycle rule expiring `<prefix>/runs/`** (raw run outputs) after a number of days, 30 by
|
||||
default. The rule must be a plain prefix filter, and no rule may expire the whole store (`repos/`, `facts/`,
|
||||
`index.jsonl` and the blobs are kept);
|
||||
- **S3 Select** (`SelectObjectContent` over JSON Lines). Every filtered read is pushed down to the server, and
|
||||
there is no fetch-and-filter fallback. A server without Select (some MinIO releases removed it) is refused;
|
||||
- **object tags readable**, because rows are filtered by their `kind` tag.
|
||||
|
||||
One-time admin setup (admin credentials, bucket `sast-memory`, store at the bucket root):
|
||||
|
||||
aws s3api put-bucket-versioning --endpoint-url "$MINIO_ENDPOINT" --bucket sast-memory \
|
||||
--versioning-configuration Status=Enabled
|
||||
aws s3api put-bucket-lifecycle-configuration --endpoint-url "$MINIO_ENDPOINT" --bucket sast-memory \
|
||||
--lifecycle-configuration '{"Rules":[{"ID":"ousast-runs-expiry","Status":"Enabled",
|
||||
"Filter":{"Prefix":"runs/"},"Expiration":{"Days":30}}]}'
|
||||
|
||||
`put-bucket-lifecycle-configuration` replaces every rule on the bucket, so merge this rule with any rules
|
||||
already there. The agent account's policy can read the bucket's configuration but not change it. It reads and
|
||||
writes objects and their tags, and it gets no `Put*` on versioning or lifecycle:
|
||||
|
||||
{"Version": "2012-10-17", "Statement": [
|
||||
{"Effect": "Allow", "Action": ["s3:ListBucket", "s3:GetBucketLocation", "s3:GetBucketVersioning",
|
||||
"s3:GetLifecycleConfiguration"],
|
||||
"Resource": ["arn:aws:s3:::sast-memory"]},
|
||||
{"Effect": "Allow", "Action": ["s3:GetObject", "s3:GetObjectVersion", "s3:PutObject", "s3:DeleteObject",
|
||||
"s3:GetObjectTagging", "s3:PutObjectTagging"],
|
||||
"Resource": ["arn:aws:s3:::sast-memory/*"]}]}
|
||||
|
||||
Measured state on 2026-10-02 (agent `sast-memory-agent`): versioning was `Enabled` and a 30-day rule on `runs/`
|
||||
was in place. `verify_bucket` still refused, because `GetObjectTagging` returned AccessDenied for the agent.
|
||||
|
||||
Known RustFS limit (measured 2026-10-02): Select infers an object's JSON schema from its leading rows. A
|
||||
`where` field that is missing there, even if row 5000 has it, fails with `EvaluatorBindingDoesNotExist` instead
|
||||
of matching. The store raises a `MemoryStoreError` that names the field. It does not answer "no rows", since
|
||||
that answer could silently drop matches.
|
||||
|
||||
The real-server contract tests (`OUSAST_MEMORY_TEST_MINIO=1`, bucket `OUSAST_MEMORY_TEST_BUCKET`, else
|
||||
`MINIO_BUCKET`) verify the bucket at its root and write only under a fresh `contract-<id>/` prefix. They never
|
||||
configure the bucket.
|
||||
|
||||
## Moving to a separate Kubernetes cluster (planned)
|
||||
|
||||
The maintainer will deploy the plane to its own Kubernetes environment. The manifests under `plane/` move as they
|
||||
|
||||
Reference in New Issue
Block a user