Granular Access Control
VSKI provides field-level access control that allows you to control which fields are visible or editable by different user groups. This works in addition to the standard collection-level rules.
Overview
Section titled “Overview”Granular access control gives you fine-grained control over:
- View Rules (
fieldRules) - Control which fields users can READ (view) - Edit Rules (
fieldEditRules) - Control which fields users can WRITE (create/update)
Key Benefits
Section titled “Key Benefits”- Data Privacy - Hide sensitive fields (like passwords, internal notes) from certain user groups
- Data Integrity - Prevent users from modifying critical fields (like status, approval flags)
- Flexible Security - Override global rules with field-level restrictions
- Group-Based - Different access levels for different user roles
How Field Rules Work
Section titled “How Field Rules Work”Rule Priority
Section titled “Rule Priority”Field-level rules have higher priority than global collection rules:
Collection-level rules: ├─ listRule ├─ viewRule ├─ createRule ├─ updateRule ├─ deleteRule └─ executeRule
Field-level rules (HIGHER PRIORITY): ├─ fieldRules (VIEW) → Overrides viewRule, listRule └─ fieldEditRules (EDIT) → Overrides createRule, updateRuleWhen field rules are defined for a user’s groups, global rules are bypassed.
Rule Format
Section titled “Rule Format”Field rules live in the collection’s options block in the IaC YAML (see
IaC), as a map of group name to allowed fields:
options: fieldRules: groupName1: [field1, field2, field3] groupName2: ["*"] # Wildcard = all fields "*": [publicField] # Public group applies to all usersSystem Fields
Section titled “System Fields”The following system fields are always included in view rules and always read-only in edit rules:
id- Record identifiercreated- Creation timestampupdated- Last update timestamp
These fields cannot be removed from field rules.
View Rules (fieldRules)
Section titled “View Rules (fieldRules)”View rules control READ access - which fields users can see when listing or viewing records.
Creating Collection with View Rules
Section titled “Creating Collection with View Rules”version: 1database: default
collections: user_profiles: type: base fields: - {name: username, type: text, required: true} - {name: email, type: email, required: true} - {name: phone, type: text} - {name: ssn, type: text} # Sensitive! - {name: notes, type: text} # Internal! rules: list: "1=1" view: "1=1" create: "1=0" # Admin only update: "1=0" delete: "1=0" options: # Field-level view rules fieldRules: public: [username] # Public can only see username viewer: [username, email, phone] # Viewers can't see SSN or notes admin: ["*"] # Admins see everythingvski applyHow View Rules Affect Queries
Section titled “How View Rules Affect Queries”When a user queries records with field-level restrictions:
# User in "viewer" grouppage = await client.collection("user_profiles").get_list(1, 10)
# Result: only username, email, phone, id, created, updated# SSN and notes are filtered out automatically# page["items"][0] =># {# "id": "abc123",# "username": "john_doe",# "email": "john@example.com",# "phone": "+1234567890",# "created": "2026-02-22T10:00:00Z",# "updated": "2026-02-22T10:00:00Z",# # ssn and notes are NOT included# }View Rules Bypass Global Rules
Section titled “View Rules Bypass Global Rules”If a user has field-level view access, global viewRule and listRule are
bypassed:
# Global rule says "no access"rules: view: "1=0" # No one can view (except admin)
# But field rules give accessoptions: fieldRules: viewer: [title] # Viewers can see title
# Result: User in "viewer" group CAN view records# despite global viewRule being "1=0"Edit Rules (fieldEditRules)
Section titled “Edit Rules (fieldEditRules)”Edit rules control WRITE access - which fields users can set when creating or updating records.
Creating Collection with Edit Rules
Section titled “Creating Collection with Edit Rules”collections: support_tickets: type: base fields: - {name: title, type: text, required: true} - {name: description, type: text, required: true} - {name: status, type: text, required: true} # Workflow field - {name: priority, type: text, required: true} # Workflow field - {name: resolution, type: text} rules: list: "1=1" view: "1=1" create: "1=0" update: "1=0" delete: "1=0" options: # Field-level edit rules fieldEditRules: customer: [title, description] # Customers can only set title, description agent: [title, description, resolution] # Agents can't change status/priority admin: ["*"] # Admins can edit everythingCreating Records with Edit Restrictions
Section titled “Creating Records with Edit Restrictions”# User in "customer" grouprecord = await client.collection("support_tickets").create( { "title": "Login issue", "description": "I can't log in", "status": "open", # IGNORED: not in customer's edit list "priority": "high", # IGNORED: not in customer's edit list "resolution": "", # IGNORED: not in customer's edit list })
# Result: Record created with only allowed fields# {# "id": "ticket-123",# "title": "Login issue",# "description": "I can't log in",# "status": "open", # Set by DB default or trigger# "priority": "normal", # Set by DB default or trigger# "resolution": None,# "created": "2026-02-22T10:00:00Z",# "updated": "2026-02-22T10:00:00Z",# }Updating Records with Edit Restrictions
Section titled “Updating Records with Edit Restrictions”# User in "agent" groupupdated = await client.collection("support_tickets").update( "ticket-123", { "title": "Updated title", # ALLOWED "description": "Updated description", # ALLOWED "resolution": "Fixed by password reset", # ALLOWED "status": "closed", # IGNORED: not in agent's edit list "priority": "low", # IGNORED: not in agent's edit list },)
# Result: Only title, description, resolution are updated# status and priority remain unchangedEdit Rules and Required Fields
Section titled “Edit Rules and Required Fields”If a required field is not in a user’s edit field list, the server logs a warning but allows the operation. The field may still get a value through:
- Database default values
- Trigger hooks
- Migration defaults
Example:
# Field "status" is requiredfields: - {name: status, type: text, required: true}
# But customer group can't edit itoptions: fieldEditRules: customer: [title, description] # status NOT included# Customer creates record - warning logged but allowedawait client.collection("tickets").create( { "title": "Issue", "description": "Details", # status not provided, but DB has default "open" })
# Server logs: "Creating record with required fields not in allowed edit fields: status"# But record is created successfully with status = "open" (default)Edit Rules Bypass Global Rules
Section titled “Edit Rules Bypass Global Rules”If a user has field-level edit access, global createRule and updateRule are
bypassed:
# Global rule says "no access"rules: create: "1=0" # No one can create (except admin) update: "1=0" # No one can update (except admin)
# But field rules give accessoptions: fieldEditRules: creator: [title] # Creators can create/edit title
# Result: User in "creator" group CAN create/update records# despite global createRule/updateRule being "1=0"Using Wildcards
Section titled “Using Wildcards”The * character has special meanings:
Wildcard in Group Name ("*")
Section titled “Wildcard in Group Name ("*")”The "*" group represents all users, including unauthenticated (guest)
users:
options: fieldRules: "*": [title, description] # Everyone can see these admin: ["*"] # Admins see everythingWildcard in Field List (["*"])
Section titled “Wildcard in Field List (["*"])”A field list containing only ["*"] means all fields are allowed:
options: fieldEditRules: editor: ["*"] # Editors can edit every field viewer: [title, description] # Viewers restrictedGroup Priority and Union
Section titled “Group Priority and Union”Users can belong to multiple groups. Field rules are combined using union logic:
options: fieldRules: "*": [title] # Public - everyone gets this team_member: [description] # Team members get this admin: [notes, internal] # Admins get this
# User in "team_member" group:# Can see: title (from *) + description (from team_member)# Cannot see: notes, internal (only admin)Practical Examples
Section titled “Practical Examples”HR System
Section titled “HR System”Public can see name/title, HR sees salary, managers see all:
collections: employees: type: base fields: - {name: name, type: text, required: true} - {name: title, type: text, required: true} - {name: department, type: text, required: true} - {name: salary, type: number} # Sensitive! - {name: performance_notes, type: text} # Internal! rules: list: "1=0" view: "1=0" create: "1=0" update: "1=0" delete: "1=0" options: fieldRules: public: [name, title] # Anyone can see name/title hr: [name, title, department, salary] # HR can see salary manager: ["*"] # Managers see everything fieldEditRules: hr: [name, title, department, salary] # HR can edit these manager: ["*"] # Managers can edit everythingCustomer Portal
Section titled “Customer Portal”Customers can read limited data, support can read all:
collections: orders: type: base fields: - {name: customerId, type: text, required: true} - {name: items, type: json, required: true} - {name: total, type: number, required: true} - {name: status, type: text, required: true} - {name: internal_notes, type: text} # Internal! rules: list: "1=0" view: "1=0" create: "1=0" update: "1=0" delete: "1=0" options: fieldRules: "*": [customerId, items, total, status] # Public can't see notes support: ["*"] # Support sees everything fieldEditRules: customer: [] # Customers can't create/edit (only view) support: [status, internal_notes] # Support can update theseContent Management System
Section titled “Content Management System”Editors can create content, editors can edit content, admins manage everything:
collections: articles: type: base fields: - {name: title, type: text, required: true} - {name: content, type: text, required: true} - {name: published, type: bool, required: true} - {name: author, type: text, required: true} - {name: featured, type: bool} # Admin only! rules: list: "published = true" view: "published = true || author = @request.auth.id" create: null update: null delete: "1=0" options: fieldEditRules: author: [title, content, published] # Authors can't change author or featured editor: [title, content, published] # Same for editors admin: ["*"] # Admins control everythingWeb UI Configuration
Section titled “Web UI Configuration”Field-level permissions are configured in the Collection Settings page:
- Navigate to Settings → Collections
- Click on a collection to edit
- Go to Access tab
- Scroll to Field-Level Permissions section
UI Sections
Section titled “UI Sections”The field permissions UI has two separate sections:
View Rules (blue)
Section titled “View Rules (blue)”Controls which fields users can READ:
- Shows which fields are visible for each group
- System fields (id, created, updated) are always included
- Badge: “Controls READ visibility”
Edit Rules (orange)
Section titled “Edit Rules (orange)”Controls which fields users can CREATE/UPDATE:
- Shows which fields are editable for each group
- System fields are read-only
- Warnings when required fields are missing from a group’s allowed list
- Badge: “Controls CREATE/UPDATE access”
Managing Group Rules
Section titled “Managing Group Rules”For each group (or * for public):
- Add Group Rule - Create a new permission group
- Toggle Fields - Click field buttons to add/remove from allowed list
- All Fields (*) - Click to allow all fields for this group
- Remove Group - Delete a permission group
Required Field Warnings
Section titled “Required Field Warnings”When a required field is not in a group’s edit rules:
⚠ Required fields not editable:
field1, field2Users in this group won’t be able to set these fields during create/update.
The record can still be created if the field has a default value or is set via triggers.
API Reference
Section titled “API Reference”Field rules are included in the collection configuration (see
CollectionConfig in the SDK):
class CollectionConfig(TypedDict, total=False): # ... other properties fieldRules: dict[str, list[str]] # View rules fieldEditRules: dict[str, list[str]] # Edit rulesCreating Collection with Field Rules
Section titled “Creating Collection with Field Rules”Declared in the IaC YAML like any other collection setting:
collections: my_collection: type: base fields: - {name: title, type: text} - {name: description, type: text} options: fieldRules: viewer: [title, description] fieldEditRules: viewer: [title]Updating Field Rules
Section titled “Updating Field Rules”Edit the options block in the YAML and apply; the block is authoritative:
collections: my_collection: options: fieldRules: viewer: [title] # More restrictive editor: [title, description, author] fieldEditRules: viewer: [title] editor: [title, description]vski plan # ~ collection default/my_collection: options updatevski applyBehavior Summary
Section titled “Behavior Summary”View Rules (fieldRules)
Section titled “View Rules (fieldRules)”| Scenario | Behavior |
|---|---|
| No rules defined | All fields visible (default open) |
| User has field access | Global viewRule/listRule bypassed, filtered fields returned |
| User has no field access | Access denied (empty result) |
| Wildcard in field list | All fields visible |
| User in multiple groups | Union of all group fields visible |
| Admin user | All fields visible (bypasses all rules) |
Edit Rules (fieldEditRules)
Section titled “Edit Rules (fieldEditRules)”| Scenario | Behavior |
|---|---|
| No rules defined | All fields editable (default open) |
| User has field access | Global createRule/updateRule bypassed, only allowed fields updated |
| User has no field access | Access denied (create/update blocked) |
| Wildcard in field list | All fields editable |
| User in multiple groups | Union of all group fields editable |
| Required field not in list | Warning logged, operation proceeds (field may have default) |
| System fields | Always read-only, cannot be edited |
| Admin user | All fields editable (bypasses all rules) |
Security Considerations
Section titled “Security Considerations”1. Defense in Depth
Section titled “1. Defense in Depth”Field rules are an additional layer of security:
- Use field rules in addition to global rules
- Don’t rely solely on field rules for security
2. Default to Deny
Section titled “2. Default to Deny”When configuring field rules:
- Start with empty field list (deny all)
- Add only necessary fields (allow by exception)
- More secure than starting with
["*"]and removing
3. Required Field Handling
Section titled “3. Required Field Handling”If a required field is not in edit rules:
- The field may get a default value from DB schema
- Consider using triggers to set appropriate defaults
- Or add the field to the user’s allowed list
4. System Field Protection
Section titled “4. System Field Protection”System fields (id, created, updated):
- Are always read-only for edit rules
- Cannot be removed from view rules
- This prevents record tampering
5. Test Access Patterns
Section titled “5. Test Access Patterns”Before deploying:
- Test with users in each group
- Verify field visibility (read operations)
- Verify field editing (create/update operations)
- Test global rule bypass scenarios
- Verify admin user behavior (should bypass all)
6. Monitor Warnings
Section titled “6. Monitor Warnings”Server logs warnings when:
- Required fields are not in edit rules
- Users attempt to edit restricted fields (silently ignored)
- Monitor these logs to catch configuration issues
Common Patterns
Section titled “Common Patterns”Public View, Private Edit
Section titled “Public View, Private Edit”options: fieldRules: "*": [title, description] # Public can read fieldEditRules: editor: [title, description] # Only editors can writeProgressive Disclosure
Section titled “Progressive Disclosure”options: fieldRules: viewer: [title] # Basic info member: [title, description] # More info admin: ["*"] # Full accessField Lifecycle Control
Section titled “Field Lifecycle Control”options: fieldRules: "*": ["*"] # Everyone can read everything fieldEditRules: creator: [title, content] # Can create initial content editor: [content] # Can edit content only publisher: [published] # Can only change statusTroubleshooting
Section titled “Troubleshooting”“Access Denied” on List/Get
Section titled ““Access Denied” on List/Get”Problem: User gets access denied error when listing or viewing records.
Solution: Check if user’s group is in fieldRules:
# User groupsuser_groups = ["viewer"]
# Check field rulescollection = await client.settings.collections.get("my_collection")print("Field rules:", collection["fieldRules"])
# Does user's group exist in fieldRules?has_access = any(group in collection["fieldRules"] for group in user_groups)print("Has access:", has_access)Fields Not Saving on Update
Section titled “Fields Not Saving on Update”Problem: User updates a record but some fields don’t change.
Solution: Check fieldEditRules - those fields may not be in allowed list:
# User in "viewer" groupawait client.collection("my_collection").update( "record-id", { "title": "Updated", # Saved "status": "active", # NOT saved - not in viewer's edit rules },)Required Fields Null After Create
Section titled “Required Fields Null After Create”Problem: After creating a record, required fields are null.
Solution: Field may have default value. Check:
- Field default in schema
- Trigger that sets the value
- Or add field to user’s
fieldEditRules
Wildcard Not Working
Section titled “Wildcard Not Working”Problem: User with "*" in field list can’t see all fields.
Solution: Ensure wildcard is in the list, not a bare string:
# Wrongoptions: fieldRules: viewer: "*"
# Correctoptions: fieldRules: viewer: ["*"]Global Rules Still Applying
Section titled “Global Rules Still Applying”Problem: Global rules (createRule, updateRule) still apply even with field rules defined.
Solution: Ensure user has ANY field-level access. Field rules override global rules only when user has field-level permission:
# User in "any_group" has field access: global createRule bypassedoptions: fieldRules: any_group: [title]
# User in "no_access" has NO field access: global createRule appliesoptions: fieldRules: admin_only: ["*"]See Also
Section titled “See Also”- Rules and Access Control - Collection-level rules
- Authentication - User management and groups
- Collections - Collection configuration