Loading...
Loading...
Safegres is Constructive's security protocol for expressing authorization as Authz* policy nodes (types + JSON configs). This skill defines each Authz* type, its config shape, semantics, and when to use it. No SQL and no SDK/grant/RLS steps.
npx skill4agent add constructive-io/constructive-skills constructive-safegresAuthzEntityMembershipdataconstructive-securityconstructive-relationsconstructive-data-modulescurrent_user_id()actor_identity_idmembership_type123owner_idAuthzEntityMembershipAuthzMembershipAuthzEntityMembershipAuthzMembershipAuthzMembership(membership_type=2)AuthzEntityMembershipentity_fieldentity_idorganization_idowner_idNote:exists as an advanced meta-node for boolean trees; see the section at the end.AuthzComposite
AuthzDirectOwner{ "entity_field": "owner_id" }{entity_field}AuthzEntityMembershipAuthzDirectOwnerAny{ "entity_fields": ["sender_id", "receiver_id"] }AuthzMembership{ "membership_type": 1 }{ "membership_type": 1, "permission": "admin_permissions" }permissionpermissionsis_adminis_ownerAuthzEntityMembership{ "entity_field": "entity_id", "membership_type": 2 }permissionpermissionsis_adminis_owner{entity_field}owner_idAuthzRelatedEntityMembership{
"entity_field": "post_id",
"membership_type": 2,
"obj_schema": "public",
"obj_table": "posts",
"obj_field": "organization_id"
}entity_idAuthzPeerOwnership{ "owner_field": "owner_id", "membership_type": 2 }permissionpermissionsis_adminis_owner{owner_field}AuthzRelatedPeerOwnershipAuthzRelatedPeerOwnership{
"entity_field": "message_id",
"membership_type": 2,
"obj_schema": "public",
"obj_table": "messages",
"obj_field": "sender_id"
}obj_ref_fieldidpermissionpermissionsis_adminis_ownerAuthzPeerOwnershipobj_field{entity_field}AuthzOrgHierarchy{ "direction": "down", "anchor_field": "owner_id", "entity_field": "entity_id" }owner_idAuthzTemporal{ "valid_from_field": "valid_from", "valid_until_field": "valid_until" }valid_from_fieldvalid_until_fieldvalid_untilvalid_until IS NULL OR valid_until > now()valid_from_inclusivetruevalid_until_inclusivefalsevalid_from_fieldCombination guidance:answers when access is valid, not who has access. On its own it means "anyone can access within the time window." In practice, always combine it with an identity-based policy — either as a restrictive top-level policy (ANDed with a permissive identity policy) or inside anAuthzTemporalAuthzComposite.BoolExpr
Overlap with: You could approximate published-content gating withAuthzPublishable(e.g.AuthzTemporalwith novalid_from_field: "published_at"). However,valid_until_fieldadditionally provides theAuthzPublishableboolean toggle, which lets authors unpublish content independently of time. Useis_publishedwhen you need an explicit on/off switch; useAuthzPublishablewhen access is purely time-driven.AuthzTemporal
AuthzPublishable{}is_published_field"is_published"published_at_field"published_at"require_published_attruerequire_published_at=truepublished_at <= nowCombination guidance:answers whether content is published, not who can see it. On its own it means "anyone can see published content." In practice, always combine it with an identity-based policy — either as a restrictive top-level policy (ANDed with a permissive identity policy likeAuthzPublishable) or inside anAuthzEntityMembershipAuthzComposite. See the "Permissive vs Restrictive policies in RLS" section for examples.BoolExpr
Overlap with: The time component ofAuthzTemporal(AuthzPublishable) is a subset of whatpublished_at <= nowcan express. The key difference is theAuthzTemporalboolean -- a deliberate on/off toggle thatis_publisheddoes not provide. If you only need time-window access with no manual toggle,AuthzTemporalis sufficient.AuthzTemporal
AuthzMemberListNot recommended. This policy relies on a UUID array column rather than a proper foreign-key relationship. It does not scale well and bypasses normal relational integrity. PreferorAuthzEntityMembershipwith proper FK-based membership tables when possible.AuthzPeerOwnership
{ "array_field": "member_ids" }{array_field}AuthzRelatedMemberListNot recommended. Same concern as-- relies on a UUID array column in a related table rather than proper FK-based membership. Prefer FK-based policies when possible.AuthzMemberList
{
"owned_schema": "public",
"owned_table": "documents",
"owned_table_key": "member_ids",
"owned_table_ref_key": "document_id",
"this_object_key": "id"
}AuthzAllowAllWARNING:is almost never what you want. It grants unconditional access to every authenticated user for the specified privilege. Before using it, ask yourself: "Should literally every authenticated user be able to read/write this data?" If the answer is no (and it usually is), use a scoped policy likeAuthzAllowAllorAuthzDirectOwnerinstead.AuthzEntityMembershipEspecially avoidon junction tables. When creating ManyToMany relations with security, match the junction table's policy to the parent tables' policies. If parents useAuthzAllowAll, the junction should too. UsingAuthzDirectOwneron a junction table means any authenticated user can create/delete links between rows they don't own. See theAuthzAllowAllskill for junction table security patterns.constructive-relations
{}countriesAuthzAllowAllAuthzAllowAllAuthzAllowAllAuthzDenyAll{}AuthzCompositeAuthzCompositedataAuthzCompositeBoolExpr{
"AuthzEntityMembership": {
"entity_field": "owner_id",
"membership_type": "Organization Member"
}
}BoolExpr{
"BoolExpr": {
"boolop": "AND_EXPR",
"args": [
{ "AuthzTemporal": { "valid_from_field": "publish_at" } },
{ "AuthzDirectOwner": { "entity_field": "owner_id" } }
]
}
}BoolExpr{
"BoolExpr": {
"boolop": "OR_EXPR",
"args": [
{
"AuthzEntityMembership": {
"entity_field": "owner_id",
"membership_type": "Organization Member"
}
},
{
"AuthzMembership": {
"membership_type": "App Member",
"permission": "create_invites"
}
}
]
}
}AuthzComposite(A OR B) AND (C OR D)permissive := falsePolicy 1 (permissive): AuthzDirectOwner { entity_field: "owner_id" }
Policy 2 (permissive): AuthzEntityMembership { entity_field: "organization_id", membership_type: 2, is_admin: true }
Effective rule: row.owner_id = actor OR actor is admin of row.organization_idPolicy 1 (permissive): AuthzEntityMembership { entity_field: "entity_id", membership_type: 2 }
Policy 2 (restrictive): AuthzTemporal { valid_from_field: "starts_at", valid_until_field: "ends_at" }
Effective rule: actor is member of row.entity_id AND now() is within [starts_at, ends_at)Policy 1 (permissive): AuthzDirectOwner { entity_field: "owner_id" }
Policy 2 (permissive): AuthzEntityMembership { entity_field: "organization_id", membership_type: 2 }
Policy 3 (restrictive): AuthzPublishable {}
Effective rule: (row.owner_id = actor OR actor is member of row.organization_id) AND row.is_published = truePolicy 1 (permissive): AuthzDirectOwner { entity_field: "owner_id" }
Policy 2 (permissive): AuthzEntityMembership { entity_field: "organization_id", membership_type: 2 }
Policy 3 (restrictive): AuthzPublishable {}
Policy 4 (restrictive): AuthzTemporal { valid_from_field: "available_from", valid_until_field: "available_until" }
Effective rule: (P1 OR P2) AND R3 AND R4
= (owner OR org member) AND is_published AND now() in time window(P1 OR P2 OR ... Pn) AND R1 AND R2 AND ... RmAuthzCompositeDesired: (AuthzEntityMembership AND AuthzPublishable) OR (AuthzDirectOwner AND AuthzTemporal)(any P) AND (all R)AuthzComposite{
"BoolExpr": {
"boolop": "OR_EXPR",
"args": [
{
"BoolExpr": {
"boolop": "AND_EXPR",
"args": [
{ "AuthzEntityMembership": { "entity_field": "organization_id", "membership_type": 2 } },
{ "AuthzPublishable": {} }
]
}
},
{
"BoolExpr": {
"boolop": "AND_EXPR",
"args": [
{ "AuthzDirectOwner": { "entity_field": "owner_id" } },
{ "AuthzTemporal": { "valid_from_field": "starts_at", "valid_until_field": "ends_at" } }
]
}
}
]
}
}AuthzCompositeAuthzComposite