eden/mononoke/docs/4.3-hooks.md
This document explains Mononoke's repository hooks system. Hooks are configurable checks that execute during bookmark updates to enforce repository policies, validate commits, and prevent problematic changes from being accepted.
Hooks are validation mechanisms that run when commits become ancestors of public bookmarks (branches). When a developer pushes commits or a bookmark is moved, Mononoke executes the configured hooks for that bookmark. If any hook rejects a commit, the entire push operation fails.
Hooks serve as the enforcement point for repository policies. They can validate commit messages, check file contents, enforce naming conventions, limit file sizes, verify permissions, and implement custom business logic. Unlike client-side hooks that can be bypassed, Mononoke's server-side hooks execute on every push.
Important: This document describes Mononoke's repository Hooks system (bookmark hooks, changeset hooks, and file hooks). There is a separate system called Pushrebase hooks that serves a different purpose. While the naming is similar, these are distinct systems:
Both systems run during pushrebase operations, but they serve different purposes and execute at different points in the process.
Mononoke supports three types of hooks, each operating at a different granularity:
Bookmark hooks execute once per bookmark movement. They have access to the bookmark being updated and the target changeset. Bookmark hooks typically validate bookmark-specific policies such as ensuring certain bookmarks are only moved by authorized services or verifying that tags follow specific naming conventions.
The BookmarkHook trait is defined in repo_attributes/hook_manager/hook_manager/src/lib.rs.
Changeset hooks execute once per changeset being pushed. They have access to the full changeset metadata, including author information, commit message, and the set of file changes. Changeset hooks typically validate commit message format, check for merge commits when linear history is required, verify author email domains, or enforce commit size limits.
The ChangesetHook trait is defined in repo_attributes/hook_manager/hook_manager/src/lib.rs.
File hooks execute once per file change (addition, modification, or deletion). They receive the file path and optionally the file content. File hooks typically enforce filename restrictions, check file content for prohibited patterns, limit individual file sizes, validate file permissions, or check for specific file types.
The FileHook trait is defined in repo_attributes/hook_manager/hook_manager/src/lib.rs.
The HookManager facet (repo_attributes/hook_manager/) manages hook registration, configuration, and execution. Like other repository facets, functions that need to run hooks declare their dependency on the hook manager through trait bounds.
The hook manager maintains:
Hook execution is integrated into bookmark movement operations in repo_attributes/bookmarks/bookmarks_movement/. When a bookmark is moved (during a push, pushrebase, or other operation), the bookmark movement code invokes the hook manager to run all applicable hooks.
Hooks are configured per-repository in the repository's metadata configuration. Configuration includes:
Hook Registration Each hook has a name and a type (bookmark, changeset, or file). The hook implementation is specified, along with any hook-specific configuration options.
Bookmark Assignment Hooks are associated with bookmarks or bookmark patterns. A hook can be assigned to:
This allows different branches to have different validation requirements. Production branches might have stricter hooks than development branches.
Hook Options Hooks can accept configuration parameters in JSON format or through typed configuration maps (strings, integers, lists). For example, a file size limit hook would be configured with the maximum allowed size, while a filename pattern hook would be configured with the patterns to allow or deny.
Log-Only Mode Hooks can be configured in log-only mode. In this mode, hook rejections are logged but do not prevent the push from succeeding. This is useful for testing new hooks before enforcing them or for gathering metrics on policy violations without disrupting developer workflows.
Bypass Settings Hooks can be configured with bypass conditions. For example, hooks might be bypassed for pushes authored by specific services rather than users, or for cross-repository sync operations.
The HookConfig struct in metaconfig/types/src/lib.rs defines the configuration structure.
When a bookmark is updated, the hook execution process follows these steps:
Hook Selection The hook manager determines which hooks apply to the bookmark being moved. This includes hooks explicitly assigned to the bookmark and hooks matching the bookmark through regular expressions.
Changeset and File Enumeration For changeset and file hooks, the system identifies which changesets are new (not already ancestors of other public bookmarks). For each changeset, file changes are enumerated.
Hook Invocation Hooks are invoked asynchronously. Multiple hooks can run concurrently. Each hook receives:
HookRepo interface)Result Collection Each hook returns either acceptance or rejection. Rejections include a short description (for grouping similar failures) and a long description (for presenting to the user with guidance on fixing the issue).
Decision If any hook rejects, the entire bookmark movement fails. All rejections are collected and returned to the client. If all hooks accept (or are in log-only mode), the bookmark movement proceeds.
Hook execution is logged to Scuba for monitoring and analysis. Metrics include execution time, success/failure status, and any rejection messages.
Mononoke includes numerous hook implementations in features/hooks/src/implementations/. These provide common validation patterns:
File Validation
deny_files - Blocks files matching specific path patternsblock_files - Prevents specific files from being modifiedblock_content_pattern - Blocks file content matching specific patternsno_bad_filenames - Rejects filenames with problematic charactersno_questionable_filenames - Validates filenames to prevent questionable charactersno_windows_filenames - Prevents filenames invalid on Windowsno_insecure_filenames - Blocks potentially dangerous filenamesblock_invalid_symlinks - Validates symbolic link targetsSize Limits
limit_filesize - Enforces maximum individual file sizelimit_commit_size - Limits total size of changes in a commitlimit_directory_size - Limits total size of a directorylimit_path_length - Enforces maximum path lengthlimit_subtree_op_size - Limits the size of subtree operations based on file count and path depthCommit Message Validation
require_commit_message_pattern - Requires commit messages to match a patternblock_commit_message_pattern - Rejects commit messages matching a patternlimit_commit_message_length - Enforces commit message length limitsCommit Structure
block_merge_commits - Prevents merge commits (enforces linear history)block_unclean_merge_commits - Allows only clean mergesblock_empty_commit - Rejects empty commitsRepository Structure
block_accidental_new_bookmark_creation - Prevents unintended bookmark creationblock_new_bookmark_creations_by_name - Restricts bookmark creation by name patternlimit_submodule_edits - Restricts changes to Git submodulesBinary and Executable Files
no_executable_binaries - Prevents executable binary filesGit-Specific
block_unannotated_tags - Requires annotated tagslimit_tag_updates - Restricts tag modificationsLFS
missing_lfsconfig - Validates Git LFS configurationTesting and Debugging
always_fail_changeset - Always rejects commits (used for testing hook execution)Additional implementations exist in features/hooks/src/facebook/implementations/ for Facebook-internal policies.
Under certain conditions, hooks can be bypassed:
Administrative Bypass
Users who are members of the admin group or have the write_no_hooks permission can set the BYPASS_ALL_HOOKS pushvar to skip hook execution. This is used for emergency fixes or automated processes that need to bypass validation. Bypass usage is logged to Scuba for auditing.
Service Pushes
Hooks can distinguish between pushes authored by users and pushes authored by services. Many hooks skip execution for service pushes, as service-originated commits are considered trusted. Hooks that still need to run for services (such as data integrity validators) can check the PushAuthoredBy parameter.
Cross-Repository Sync Hooks can distinguish between commits pushed directly to a repository and commits synchronized from another repository (push-redirected commits). Hooks may skip execution for synchronized commits, as they were already validated in the source repository.
The bypass logic is implemented in repo_attributes/bookmarks/bookmarks_movement/src/hook_running.rs.
New hooks are implemented by:
BookmarkHook, ChangesetHook, or FileHook)run method to perform validation and return HookExecution::Accepted or HookExecution::RejectedHook implementations receive a HookRepo interface providing access to repository data without requiring all repository facets. This interface is defined in repo_attributes/hook_manager/hook_manager/src/repo.rs and provides methods for fetching file contents, listing changesets, and accessing other data needed for validation.
Hooks should be stateless—all necessary state should be passed through configuration. This allows hook configuration to be changed without code deployment.
Hooks are integrated into several repository operations:
Pushrebase
When commits are landed via pushrebase (features/pushrebase/), hooks run before the server-side rebase is computed. Hooks validate the original pushed commits, and if they reject, the pushrebase is aborted before any rebasing occurs. If hooks pass, the server-side rebase proceeds, and then pushrebase hooks (a separate system) add metadata to the rebased commits before the bookmark is moved.
Bookmark Movement
Direct bookmark updates (in repo_attributes/bookmarks/bookmarks_movement/) run hooks after validating other constraints (permissions, restrictions) but before updating the bookmark. This ensures all validations occur before the repository state changes.
Landing Service
The land service (servers/land_service/) coordinates hook execution for landing operations, serializing execution when necessary to prevent conflicts.
Hooks execute within a transaction boundary—if they reject, no repository state is modified.
Hook execution is instrumented for monitoring and debugging:
Scuba Logging Each hook execution is logged with:
Bypassed Commits When hooks are bypassed (via admin override or configuration), the bypassed commits are logged separately to a Scuba table for security auditing.
Performance Metrics Hook execution time is tracked to identify slow hooks. Hooks that significantly impact push latency can be optimized or moved to asynchronous validation.
Hooks interact with several Mononoke features:
Pushrebase Hooks validate commits during pushrebase operations, but they run before the server-side rebase occurs. When a push triggers pushrebase, hooks execute on the original pushed commits before rebasing happens. If any hook rejects a commit, the pushrebase is aborted and the bookmark remains unchanged. This validation-before-rebase approach ensures that policy violations are caught early in the process.
After the regular hooks pass and the rebase computation completes, the separate pushrebase hooks system (documented in Pushrebase) runs to add metadata to the rebased commits.
Cross-Repository Sync Hooks can be configured to skip execution for commits synchronized from other repositories, avoiding duplicate validation when commits flow through multiple repositories.
Derived Data Hooks execute on the write path and do not wait for derived data. They operate on Bonsai changesets and file content, which are available immediately. If a hook needs indexed data, it must either compute it inline or rely on pre-existing derived data.
Permissions
Hook bypass requires specific permissions (admin group membership or write_no_hooks action). The hook manager integrates with the permission system to verify bypass authorization.
Hooks provide server-side policy enforcement in Mononoke. The hook system:
Three Hook Types - Bookmark, changeset, and file hooks operate at different granularities.
Configurable Execution - Hooks are assigned to bookmarks or bookmark patterns, allowing different validation for different branches.
Rejection with Feedback - Hooks that reject provide descriptions to help developers fix issues.
Bypass Mechanisms - Administrative bypass and service push detection allow flexibility when needed.
Integrated Execution - Hooks execute during bookmark movement operations, running after other validations but before repository state changes.
Built-in Implementations - Mononoke includes many standard hooks for common validation patterns.
Hooks are implemented in features/hooks/ and managed by the HookManager facet in repo_attributes/hook_manager/. Integration with bookmark movement is in repo_attributes/bookmarks/bookmarks_movement/.