Explanations

About Security Policies


What is a security policy?

A security policy is a ledger record that represents a set of access rules that can be attached to records.

{
    "data": {
        "handle": "bank",
        "schema": "access",
        "record": "wallet",
        "values": [{
            "action": "update",
            "signer": {
                "$circle": "bank-circle"
            }
        }, {
            "action": "read",
            "bearer": {
                "$signer": {
                    "$circle": "bank-circle"
                }
            }
        }],
        "custom": {
        ...
        },
        "access": [...],
    },   
    "hash": "...",
    "meta": {...}
}

Policy Features

Policies support several powerful features:

  1. Record Targeting: Policies can be scoped to specific record types using the record field
  2. Extension: Policies can extend other policies using the extend field, inheriting their rules
  3. Schema Validation: Policies use schemas to validate their structure and rules

Best Practices

When working with policies:

  1. Start with restrictive policies and gradually add permissions as needed
  2. Create your own default policy as a base for common access patterns
  3. Leverage policy extension to build on existing rules
  4. Create specialized policies for specific use cases

Policy (as well as access rules in general) can be applied at different levels:

  • Server level - Applied via server configuration, affects all ledgers
  • Ledger level - Applied to all records within a specific ledger
  • Record level - Applied to individual records only

Security Consideration: When designing access rules, be careful with ledger-level rules. Since Minka uses an additive authorization model, permissive rules at the ledger level can grant access even when individual records have restrictive rules. Always design access rules with the principle of least privilege in mind.

Targeting records - record

To create a policy it is necessary to define the target class of record which can reuse it and it's done with record and filter properties.

Below, there are some examples of policies that defines security rules to be used by ledger records.

{
    "data": {
        "handle": "signer-management",
        "record": "signer",
        "schema": "access",
        "values": [{
            "action": "update",
            "signer": {
                "$circle": "owner"
            }
        }, {
            "action": "read",
            "bearer": {
                "$signer": {
                    "$circle": "owner"
                }
            }
        }],
        "custom": {
        ...
        },
        "access": [...],
    },   
    "hash": "...",
    "meta": {...}
}
{
    "data": {
        "handle": "record-factory",
        "record": "any",
        "schema": "access",
        "values": [{
            "action": "create",
            "signer": {
                "$circle": "owner"
            }
        }],
        "custom": {
        ...
        },
        "access": [...],
    },   
    "hash": "...",
    "meta": {...}
}
{
    "data": {
        "handle": "wallet-reader",
        "record": "wallet",
        "schema": "access",
        "filter": {
            "schema": "bank-wallet" 
        },
        "values": [{
            "action": "read",
            "signer": {
                "$circle": "bank"
            }
        }],
        "custom": {
        ...
        },
        "access": [...],
    },   
    "hash": "...",
    "meta": {...}
}

Extending policies - extend

Policies can be extended, which means that one policy can reuse values of an existing policy by using the property extend

// Policy "reader"
{
    "data": {
        "handle": "reader",
        "record": "any",
        "schema": "access",
        "values": [{
            "action": "read",
            "signer": {
                "$circle": "admin"
            }
        }],
        "custom": {
        ...
        },
        "access": [...],
    },   
    "hash": "...",
    "meta": {...}
}

// Policy "wallet-reader"
{
    "data": {
        "handle": "wallet-reader", 
        "extend": "reader",
        "record": "wallet",
        "values": [{
            "action": "read",
            "signer": {
                "$circle": "bank"
            }
        }],
        "custom": {
        ...
        },
        "access": [...],
    },   
    "hash": "...",
    "meta": {...}
}

Adding filter to policy values - filter

Policy values accepts filter, which is used to filter to which records the value applies. Filter can be defined with any content, and its value is used to match properties of a record data.

// Symbol "usd"
{
    "data": {
        "handle": "usd",
        "factor": 100,
        "schema": "fiat",
        ...
    },   
    "hash": "...",
    "meta": {...}
}

// Symbol "bitcoin"
{
    "data": {
        "handle": "bitcoin",
        "factor": 100000000,
        "schema": "crypto",
        ...
    },   
    "hash": "...",
    "meta": {...}
}

// Policy "symbol-reader"
{
    "data": {
        "handle": "symbol-reader", 
        "record": "symbol",
        "schema": "access",
        "values": [{
            "action": "read",
            "signer": {
                "$circle": "bank"
            },
            "filter": {
                "schema": "fiat"
            },
        }, {
            "action": "read",
            "signer": {
                "$circle": "exchange"
            },
            "filter": {
                "schema": "crypto"
            }
        }],
        "custom": {
        ...
        },
        "access": [...],
    },   
    "hash": "...",
    "meta": {...}
}

Attaching built-in functions to policy values - invoke

Policy values accepts invoke, which can be used to attach built-in functions to policies.

Follow the functions implemented and their respective records.

RECORDFUNCTION NAMEDescription
walletwallet.canSpendAllChangedRouteTargetsA route of action forward or debit can be added to a wallet only if the signer has access to spend the target wallet of the route.
intentintent.canReadAnyClaimWalletAn intent can be read only if the user is allowed to read any of the wallets involved in any claim of this intent as source or target.
intentintent.canReadAnyClaimWalletInThreadAn intent can be read only if the user is allowed to read any of the wallets involved in any claim of the thread that this intent is part of - as source or target. For intent threads with a single intent, this function has the same effect of intent.canReadAnyClaimWallet
intentintent.canSpendEveryClaimWalletIntents can be created only if the signer has access to spend all the participant wallets of this intent - as source and target .
// Policy "wallet-mutation"
{
    "data": {
        "handle": "wallet-mutation", 
        "record": "wallet",
        "schema": "access",
        "values": [{
            "action": "create",
            "invoke": "wallet.canSpendAllChangedRouteTargets"
        }, {
            "action": "update",
            "invoke": "wallet.canSpendAllChangedRouteTargets"
        }],
        "custom": {
        ...
        },
        "access": [...],
    },   
    "hash": "...",
    "meta": {...}
}

Policy usage

A policy can be attached to access rules of ledger records by referencing its handle

{
    "data": {
        "handle": "bank-wallet",
        "custom": {
        ...
        },
        "access": [{
            "policy": "bank"
        }],
    },   
    "hash": "...",
    "meta": {...}
}

Policies can be also reused alongside regular access rules.

{
    "data": {
        "handle": "bank-wallet",
        "custom": {
        ...
        },
        "access": [{
            "policy": "bank"
        }, {
            "action": "spend",
            "signer": {
                "handle": "treasury"
            }
        }],
    },   
    "hash": "...",
    "meta": {...}
}

Security policies are powerful since they centralize security rules and ease their management. See About Authorization for more details.