{"id":22646373,"url":"https://github.com/fireblocks/plugin-based-callback-handler","last_synced_at":"2025-04-12T00:34:35.569Z","repository":{"id":237241731,"uuid":"794108904","full_name":"fireblocks/plugin-based-callback-handler","owner":"fireblocks","description":"Plugin Based Fireblocks API Cosigner Callback Handler boilerplate","archived":false,"fork":false,"pushed_at":"2024-08-26T21:28:35.000Z","size":43,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-25T20:21:25.369Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fireblocks.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-30T13:25:05.000Z","updated_at":"2025-03-03T15:35:37.000Z","dependencies_parsed_at":"2024-04-30T14:45:33.547Z","dependency_job_id":"07f9571f-b3e8-439e-ad7e-53f0534bb6aa","html_url":"https://github.com/fireblocks/plugin-based-callback-handler","commit_stats":null,"previous_names":["fireblocks/plugin-based-callback-handler"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fireblocks%2Fplugin-based-callback-handler","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fireblocks%2Fplugin-based-callback-handler/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fireblocks%2Fplugin-based-callback-handler/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fireblocks%2Fplugin-based-callback-handler/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fireblocks","download_url":"https://codeload.github.com/fireblocks/plugin-based-callback-handler/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501700,"owners_count":21114676,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-09T06:10:10.320Z","updated_at":"2025-04-12T00:34:35.535Z","avatar_url":"https://github.com/fireblocks.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./logo.svg\" width=\"350\" alt=\"accessibility text\"\u003e\n\u003c/p\u003e\n\u003cdiv align=\"center\"\u003e\n\n  [Fireblocks Developer Portal](https://developers.fireblocks.com) \u003c/br\u003e\n  [Fireblocks API Co-Signer \u0026 Callback Handler Docs](https://developers.fireblocks.com/reference/automated-signer-callback)\n\u003c/div\u003e\n\n# Fireblocks Plugin Based Callback Handler\n⚠️  `Please be aware that this package serves as an example to demonstrate the usage of the Fireblocks API Co-Signer callback handler. It is not intended for use in production environments.` ⚠️\n\n## Table Of Contents 📖\n\n- [Introduction](#introduction)\n- [Architecture](#architecture)\n- Built-in plugins:\n  - [Transaction Validation](https://github.com/fireblocks/plugin-based-callback-handler/blob/main/src/plugins/README.md#transaction-validation-plugin-txid_validationpytxidvalidation-class)\n  - [Extra Signature Validation](https://github.com/fireblocks/plugin-based-callback-handler/blob/main/src/plugins/README.md#extra-signature-validation-extra_signaturepyextrasignature-class)\n  - [Transaction Policy Validation](https://github.com/fireblocks/plugin-based-callback-handler/blob/main/src/plugins/README.md#transaction-policy-validation-plugin-tx_policy_validationpytxpolicyvalidation-class)\n  - [PSBT Validation](https://github.com/fireblocks/plugin-based-callback-handler/blob/main/src/plugins/README.md#psbt-validation-plugin-psbt_validationpypsbtvalidation-class)\n- [Contribution](#contribution)\n  - [Adding New Plugins](#new-plugins)\n  - [New DB Connections](#setting-a-new-db-connection)\n- [Usage](#usage)\n\n--- \n\n## Introduction 👋\n### Fireblocks API Co-Signer \u0026 Callback Handler\nThe Fireblocks API Co-Signer allows you to automate approvals and signing for transactions and workspace changes. \\\nThis is ideal for any workspace expecting a high volume of transactions, frequent workspace activity, or requiring 24-hour access.\\\nYou can configure the API Co-Signer with a Co-Signer Callback Handler. The Callback Handler is a predefined \\HTTPS server that receives requests from the API Co-Signer and returns an action.\n\nWhen your API Co-Signer is configured with a callback, it sends a POST request to the callback handler. The POST request contains a JSON Web Token (JWT) encoded message signed with the API Co-Signer's private key. The Callback Handler uses the API Co-Signer's public key to verify that every incoming JWT is signed correctly by the API Co-Signer.\n\nThe Callback Handler's response is a JWT-encoded message signed with the Callback Handler's private key. This private key must be paired with the public key provided to the API Co-Signer during the Callback Handler's setup.\nFor detailed documentation of the API Co-Signer Callback Handler Data objects please visit [Fireblocks Developer Portal](https://developers.fireblocks.com/reference/automated-signer-callback).\n\n### Callback Handler Plugin Application\nThe Fireblocks Plugin Based Callback Handler is a boilerplate application that simplifies the Callback Handler installation and setup for Fireblocks users. \\\nIt is engineered to seamlessly integrate with plugins, allowing users to effortlessly develop custom functionalities without dedicating resources to server application development. The Plugins application is bundled with a selection of pre-configured plugins, each detailed below for reference.\n\n---\n\n## Architecture 🏛️\nThe Plugin Based Callback Handler application is written in Python (3.11). \\\nWeb Framework - FastAPI. \n\nCo-Signer JWT authentication is implemented in the `authenticate.py`, in `authenticate_jwt` decorator.  \nThe application assumes that both the Co-Signer public key and the Callback Private key files are located on the server while the path is defined in the .env file.\n\nPlugins that require DB access can use the implemented MongoDB and Postgres integrations. Additional DB interfaces can be implemented by the user - see the 'Contribution' section. \n\nAll exiting plugins and their related code is located in `src/plugins` directory. \\\nNew plugins added by the user can be either located in the `src/plugins` directory (expects by default) or in a different location with the path provided as part of the .env PLUGINS variable.\\\nThe `PLUGINS` env variable should be structured in the following way: \\\n\u0026emsp; \u0026emsp; `PLUGINS=my_plugin1:/path/to/plugin,my_plugin2` - note that `my_plugin2` is not set with `:path` hence the application assumes that it's in `src/plugins` directory. \\\nAll plugin names should be separated by comma.\n\nAll DB interfaces (including new DB interfaces to be added) are located in `src/databases` directory.\n\nLogs configuration can be found in `src/logs/log_config.py` file. \\\nThe log structure is the following: `\"module: timestamp | filename:line | level: message\"`. \n\n`settings.py` wraps the environment variables and runs validations (Additional validations - in progress) - the server won't start if the validations fail.\n\nBy default, the app is running on port 8000. This can be changed in the `docker-compose.yaml` or in the `APP_LISTENING_PORT` variable in .env file.\n\n---\n\n## Contribution 🤝\n### New Plugins:\n1. Create a new python file in the `src/plugins` directory and name it in snake_case, for example: `my_example_plugin.py`\n2. If you want to place the plugin in a different location - please make sure to update the `PLUGINS` env variable with the path (`my_example_plugin:/path/to/plugin`)\n3. Create a class in the newly created file in PascalCase, for example: `MyExamplePlugin`\n   - Please make sure to follow the naming convention: `my_example_plugin.py` file name -\u003e `MyExamplePlugin` class name\n4. Inherit the abstract class `PluginInterface` from `src/plugins/interface.py`:\n```python\nfrom src.plugins.interface import PluginInterface\n\nclass MyExamplePlugin(PluginInterface)\n.\n.\n.\n```\n5. Make sure to implement the required methods:\n   - `async def init(self, *args, **kwargs)`: Instantiation function that is being called upon plugin instantiation. Can run any internal logic required for valid object instantiation. \n   - `async _create_db_instance(self, db_type: str)`: Creating a DB instance method, is usually being called in the init method. Declare the method without implementation in plugins that do not require DB access.  \n   - `async def process_request(self, data) -\u003e bool`: The entry point to your plugin logic. This method will be called to initiate the plugin approval flow and should return true/false according to the approval decision. \n   - `def __repr__(self)`: Representation for logging purposes\n   - `async def set_db_instance(self, db)` - Implemented to set the DB attribute. Can be overridden if required.\n6. Update the PLUGINS variable in .env file. Set the name of the plugin in snake_case. In case of running multiple plugins - separate by comma. Don't forget to provide the path to the plugin in case it is not located in `src/plugins`:\n   - For example: `PLUGINS=plugin_one,plugin_two,my_example_plugin:/path/to/plugin`\n\u003c/br\u003e\n\n#### Adding Validations:\n\n- To implement validations that execute during server startup, please add them as functions in the `src/validations.py` file and ensure they are incorporated in the `run_validations()` function.\n---\n\n### Setting a new DB connection:\n1. By default, MongoDB and Postgres are supported. If one of these works for you, update the required environment variables in .env file.\n2. If a new type of DB connection is required, please follow the steps below:\n   - Create a new file in `src/databases`, for example: `my_db_conn.py` \n   - Create a new class in the newly created file and inherit the abstract class `DatabaseInterface` from `src/databases/interface.py`\n   ```python\n   from src.databases.interface import DatabaseInterface\n   \n   class MyDbConn(DatabaseInterface):\n   .\n   .\n   . \n   ```\n   - Make sure to implement the required methods:\n     - `async def _connect(self, *args)`: Initial connection to your DB.\n     - `async def _disconnect(self, *args, **kwargs)`: Closing the DB connection.\n     - `async def build_query(self, *args, **kwargs)`: Builds the DB query.\n     - `async def execute_query(self, *args)`: Executes a query to your DB.\n   - Update the `DB_TYPE` variable in the .env file, for example: `DB_TYPE=my_db_conn`\n   - Update the `DB_CLASS_MAP` variable in `settings.py` while the key is the DB_TYPE and the value is the name of the DB connection class, for example: \n     ```python\n     DB_CLASS_MAP = {\n      'my_db_conn': MyDbConn\n     }\n     ```\n--- \n\n## Usage 🛠\n1. Update `.env` file with all the required values (see `example.env` for reference) \n2. Co-Signer public key in a file (To verify received JWT received from the Co-Signer)\n3. Callback Handler private key (To sign JWT response back to the Co-Signer)\n\u003e **_NOTE:_** Currently the application supports only local keys (both cosigner public and callback private), we recommend to place these in the `src/keys` directory and provide the full path to both in the .env file \n4. With Docker: \n   - Run `docker-compose up`\n5. Locally:\n   - Run `git clone https://github.com/fireblocks/plugin-based-callback-handler.git`\n   - Run `pip install -r requirements.txt`\n   - Run `python main.py`","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffireblocks%2Fplugin-based-callback-handler","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffireblocks%2Fplugin-based-callback-handler","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffireblocks%2Fplugin-based-callback-handler/lists"}