Nitty-gritty of rules --------------------- Ultimately, all rules boil down to either a grant or deny operation, or else some manipulation of the current tagset associated with the request. A request starts with a set of immutable data such as the user making the request and the repository being operated upon. At certain times, other immutable data will be provided in the form of things like the paths being modified and the refs being updated. In addition to the immutable data, rules can create, update and delete other tags associated with the request as the rules processing is underway. This allows for potentially complex rulesets. The moment the 'gitano/action' tag is created, that action is taken. Thus a Grant operation actually ends up setting the 'gitano/action' tag to 'allow'. The values the gitano/action tag is allowed to take are: allow -- Allow the requested operation to take place. deny -- Prevent the requested operation from happening. Any value other than 'allow' will be treated as 'deny'. In addition, if the action is to deny the operation then if gitano/reason is set, it will be provided to the user. If the gitano/callout tag is created then the current rule processing is suspended and the referenced ruleset is called out to. This allows more complex "subroutines" to be created by admins without having unpleasant huge rulesets. If a callout ends up calling out to a ruleset already in the call stack then rules processing immediately stops with a 'deny' whose reason is "Loop detected in rules processing" regardless of whether or not a loop would actually result from the callout. Each rule consists of a number of match operations and a number of tag manipulation operations. For example a rule might match gitano/operation against "write", gitano/repository against "^myproject$" (note the .git is stripped during rule evaluation) and gitano/groups against "/myproject-members/" and would then set gitano/action to allow. Such a rule might be expressed as: GrantWrite(Group "myproject-members") and be present in the myproject gitano/admin branch rules file. Rules extracted from the admin branch of a project automatically have a match against the project added to them. (and since the rules will only be loaded if that project is referenced, it all makes sense) Tags in gitano/ are all immutable except for the action and reason tags and the callout tag. Tags in $projectname/ are mutable to rules which match the projectname exactly. As such, project rules files are permitted to mutate those tags at will. When setting tag values, the string assigned to them will be set directly. When matching against tags, the tag's value is first expanded according to the following syntax: $. => . (where . is anything except an open brace) ${tagname} => expanded to the expanded value of the tag in question. If during string expansion a loop is detected, then immediately the ruleset action will be set to deny with the reason "Loop detected in tag expansion". Note that when matching is done, the pattern used to match is also expanded according to the expansion rules, so you can use tag values during matches. You can (optionally) force a value to be expanded before assignment by using the function called Expand. So at its core, you add rules in the following way: Rule({ "match", "gitano/groups", "/${gitano/project}-members/" }, { "match", "gitano/operation", "write" }, { "set", "gitano/action", "allow" }) The 'Rule' statement here, in a repository's administration branch, will implicitly add {"match", "gitano/repository", "^thisrepositoryname$"} narrowing the ruleset so that no mistakes can be made.