diff --git a/policy/actionset.go b/policy/actionset.go
index 0acd4a37..525f6eac 100644
--- a/policy/actionset.go
+++ b/policy/actionset.go
@@ -228,6 +228,19 @@ func (actionSet ActionSet) ToVectorsSlice() []VectorsAction {
return actions
}
+// ToMemorySlice - returns slice of Memory actions from the action set.
+func (actionSet ActionSet) ToMemorySlice() []MemoryAction {
+ if len(actionSet) == 0 {
+ return nil
+ }
+ actions := make([]MemoryAction, 0, len(actionSet))
+ for action := range actionSet {
+ actions = append(actions, MemoryAction(action))
+ }
+
+ return actions
+}
+
// UnmarshalJSON - decodes JSON data to ActionSet.
func (actionSet *ActionSet) UnmarshalJSON(data []byte) error {
var sset set.StringSet
@@ -293,6 +306,16 @@ func (actionSet ActionSet) ValidateVectors() error {
return nil
}
+// ValidateMemory checks if all actions are valid Memory actions
+func (actionSet ActionSet) ValidateMemory() error {
+ for _, action := range actionSet.ToMemorySlice() {
+ if !action.IsValid() {
+ return Errorf("unsupported memory action '%v'", action)
+ }
+ }
+ return nil
+}
+
// Validate checks if all actions are valid
func (actionSet ActionSet) Validate() error {
for _, action := range actionSet.ToSlice() {
diff --git a/policy/memory-action.go b/policy/memory-action.go
new file mode 100644
index 00000000..08c13ab7
--- /dev/null
+++ b/policy/memory-action.go
@@ -0,0 +1,95 @@
+// Copyright (c) 2015-2025 MinIO, Inc.
+//
+// This file is part of MinIO Object Storage stack
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package policy
+
+import (
+ "github.com/minio/pkg/v3/policy/condition"
+)
+
+// MemoryAction - AIStor Memory API policy action. The Memory API is a MinIO
+// AIStor extension serving agent memory on a Cortex (Memory Bucket).
+type MemoryAction string
+
+const (
+ // MemoryCreateCortexAction - create a Memory cortex (Memory Bucket).
+ MemoryCreateCortexAction MemoryAction = "memory:CreateCortex"
+
+ // MemoryDeleteCortexAction - delete a Memory cortex.
+ MemoryDeleteCortexAction = "memory:DeleteCortex"
+
+ // MemoryGetCortexAction - read a Memory cortex's metadata.
+ MemoryGetCortexAction = "memory:GetCortex"
+
+ // MemoryListCortexesAction - list Memory cortexes.
+ MemoryListCortexesAction = "memory:ListCortexes"
+
+ // MemoryPutSecretAction - write a secret in a cortex.
+ MemoryPutSecretAction = "memory:PutSecret"
+
+ // MemoryGetSecretAction - read a secret's decrypted value from a cortex.
+ MemoryGetSecretAction = "memory:GetSecret"
+
+ // MemoryDeleteSecretAction - delete a secret from a cortex.
+ MemoryDeleteSecretAction = "memory:DeleteSecret"
+
+ // MemoryListSecretsAction - list the secrets in a cortex.
+ MemoryListSecretsAction = "memory:ListSecrets"
+
+ // MemorySearchAction - search (corpus-grep) the objects in a cortex.
+ MemorySearchAction = "memory:Search"
+
+ // AllMemoryActions - all AIStor Memory API actions.
+ AllMemoryActions = "memory:*"
+)
+
+// SupportedMemoryActions - list of all supported AIStor Memory API actions.
+var SupportedMemoryActions = map[MemoryAction]struct{}{
+ MemoryCreateCortexAction: {},
+ MemoryDeleteCortexAction: {},
+ MemoryGetCortexAction: {},
+ MemoryListCortexesAction: {},
+ MemoryPutSecretAction: {},
+ MemoryGetSecretAction: {},
+ MemoryDeleteSecretAction: {},
+ MemoryListSecretsAction: {},
+ MemorySearchAction: {},
+ AllMemoryActions: {},
+}
+
+// IsValid - checks if action is valid or not.
+func (action MemoryAction) IsValid() bool {
+ _, ok := SupportedMemoryActions[action]
+ return ok
+}
+
+func createMemoryActionConditionKeyMap() map[Action]condition.KeySet {
+ commonKeys := []condition.Key{}
+ for _, keyName := range condition.CommonKeys {
+ commonKeys = append(commonKeys, keyName.ToKey())
+ }
+
+ memoryActionConditionKeyMap := map[Action]condition.KeySet{}
+ for act := range SupportedMemoryActions {
+ memoryActionConditionKeyMap[Action(act)] = condition.NewKeySet(commonKeys...)
+ }
+
+ return memoryActionConditionKeyMap
+}
+
+// MemoryActionConditionKeyMap - holds mapping of Memory actions to condition keys.
+var MemoryActionConditionKeyMap = createMemoryActionConditionKeyMap()
diff --git a/policy/memory-action_test.go b/policy/memory-action_test.go
new file mode 100644
index 00000000..9d4365ab
--- /dev/null
+++ b/policy/memory-action_test.go
@@ -0,0 +1,56 @@
+// Copyright (c) 2015-2026 MinIO, Inc.
+//
+// This file is part of MinIO Object Storage stack
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+package policy
+
+import (
+ "testing"
+)
+
+func TestMemoryActionIsValid(t *testing.T) {
+ testCases := []struct {
+ action MemoryAction
+ expectedResult bool
+ }{
+ {MemoryCreateCortexAction, true},
+ {MemoryDeleteCortexAction, true},
+ {MemoryGetCortexAction, true},
+ {MemoryListCortexesAction, true},
+ {MemoryPutSecretAction, true},
+ {MemoryGetSecretAction, true},
+ {MemoryDeleteSecretAction, true},
+ {MemoryListSecretsAction, true},
+ {MemorySearchAction, true},
+ {AllMemoryActions, true},
+ {MemoryAction("memory:FooBar"), false},
+ {MemoryAction("s3tables:CreateTable"), false},
+ }
+
+ for i, testCase := range testCases {
+ if result := testCase.action.IsValid(); result != testCase.expectedResult {
+ t.Fatalf("case %v: action %v: expected: %v, got: %v", i+1, testCase.action, testCase.expectedResult, result)
+ }
+ }
+}
+
+func TestMemoryActionConditionKeys(t *testing.T) {
+ for action := range SupportedMemoryActions {
+ if _, ok := MemoryActionConditionKeyMap[Action(action)]; !ok {
+ t.Fatalf("action %v: no condition key set registered", action)
+ }
+ }
+}
diff --git a/policy/resourceset.go b/policy/resourceset.go
index 7eb9966d..837ab832 100644
--- a/policy/resourceset.go
+++ b/policy/resourceset.go
@@ -207,6 +207,22 @@ func (resourceSet ResourceSet) ValidateVectors() error {
return nil
}
+// ValidateMemory - validates ResourceSet for the AIStor Memory API.
+// A Memory cortex is an S3 bucket, so resources use S3 ARN format
+// (e.g., arn:aws:s3:::my-cortex or arn:aws:s3:::my-cortex/*).
+func (resourceSet ResourceSet) ValidateMemory() error {
+ for resource := range resourceSet {
+ if !resource.isS3() {
+ return Errorf("resource '%v' type is not S3", resource)
+ }
+ if err := resource.Validate(); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
// ValidateBucket - validates ResourceSet is for given bucket or not.
func (resourceSet ResourceSet) ValidateBucket(bucketName string) error {
for resource := range resourceSet {
diff --git a/policy/statement.go b/policy/statement.go
index 04d32a96..6418e835 100644
--- a/policy/statement.go
+++ b/policy/statement.go
@@ -146,7 +146,7 @@ func (statement Statement) validateActionTypes() error {
if len(actions) == 0 {
actions = statement.NotActions
}
- var hasS3, hasAdmin, hasSTS, hasKMS, hasTable, hasVectors bool
+ var hasS3, hasAdmin, hasSTS, hasKMS, hasTable, hasVectors, hasMemory bool
for action := range actions {
switch {
case AdminAction(action).IsValid():
@@ -159,12 +159,14 @@ func (statement Statement) validateActionTypes() error {
hasTable = true
case VectorsAction(action).IsValid():
hasVectors = true
+ case MemoryAction(action).IsValid():
+ hasMemory = true
default:
hasS3 = true
}
}
count := 0
- for _, b := range []bool{hasS3, hasAdmin, hasSTS, hasKMS, hasTable, hasVectors} {
+ for _, b := range []bool{hasS3, hasAdmin, hasSTS, hasKMS, hasTable, hasVectors, hasMemory} {
if b {
count++
}
@@ -231,6 +233,15 @@ func (statement Statement) isVectors() bool {
return false
}
+func (statement Statement) isMemory() bool {
+ for action := range statement.Actions {
+ if MemoryAction(action).IsValid() {
+ return true
+ }
+ }
+ return false
+}
+
// isValid - checks whether statement is valid or not.
func (statement Statement) isValid() error {
if !statement.Effect.IsValid() {
@@ -360,6 +371,46 @@ func (statement Statement) isValid() error {
return nil
}
+ if statement.isMemory() {
+ if err := statement.Actions.ValidateMemory(); err != nil {
+ return err
+ }
+ for action := range statement.Actions {
+ keys := statement.Conditions.Keys()
+ keyDiff := keys.Difference(MemoryActionConditionKeyMap[action])
+ if !keyDiff.IsEmpty() {
+ return Errorf("unsupported condition keys '%v' used for action '%v'", keyDiff, action)
+ }
+ }
+
+ if len(statement.Resources) == 0 && len(statement.NotResources) == 0 {
+ return Errorf("Resource must not be empty")
+ }
+
+ if len(statement.Resources) > 0 && len(statement.NotResources) > 0 {
+ return Errorf("Resource and NotResource cannot be specified in the same statement")
+ }
+
+ if err := statement.Resources.ValidateMemory(); err != nil {
+ return err
+ }
+
+ if err := statement.NotResources.ValidateMemory(); err != nil {
+ return err
+ }
+
+ for action := range statement.Actions {
+ if len(statement.Resources) > 0 && !statement.Resources.ObjectResourceExists() && !statement.Resources.BucketResourceExists() {
+ return Errorf("unsupported Resource found %v for action %v", statement.Resources, action)
+ }
+ if len(statement.NotResources) > 0 && !statement.NotResources.ObjectResourceExists() && !statement.NotResources.BucketResourceExists() {
+ return Errorf("unsupported NotResource found %v for action %v", statement.NotResources, action)
+ }
+ }
+
+ return nil
+ }
+
if !statement.SID.IsValid() {
return Errorf("invalid SID %v", statement.SID)
}