Contributing to AiSOC
May 5, 2026 · View on GitHub
Thank you for your interest in contributing to AiSOC! This document provides guidelines for contributing to the project.
Code of Conduct
By participating in this project, you agree to abide by our Code of Conduct. Please be respectful and constructive in all interactions.
Getting Started
- Fork the repository on GitHub
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/AiSOC.git - Add the upstream remote:
git remote add upstream https://github.com/beenuar/AiSOC.git - Create a feature branch:
git checkout -b feature/my-feature
Development Setup
See README.md for detailed setup instructions.
Making Changes
Code Style
- TypeScript/JavaScript: ESLint + Prettier (config in root)
- Python:
rufffor linting and formatting,mypyfor type checking (config inruff.tomland per-servicepyproject.toml) - Go:
gofmt+go vet
Commit Messages
Follow Conventional Commits:
feat(alerts): add bulk status update endpoint
fix(enrichment): handle rate limit backoff
docs(readme): update deployment instructions
test(agents): add unit tests for investigation agent
Testing
- Write tests for all new features
- Maintain or improve test coverage
- Run the full test suite before submitting a PR
Public eval harness
Anything that touches the agent (services/agents/), the orchestrator
graph, prompts, tools, RAG corpus, or detection content must be
re-graded against the public eval harness. The harness lives under
scripts/run_evals.py and per-axis tests under
services/agents/tests/.
# Run all four substrate eval suites against the bundled 200-incident
# dataset and write a JSON report. The dataset size is fixed by
# services/agents/tests/eval_data/synthetic_incidents.json — there is no
# --count flag.
python scripts/run_evals.py --out eval_report.json
# Or run a single eval axis
pytest services/agents/tests/test_mitre_accuracy.py
pytest services/agents/tests/test_alert_reduction.py
pytest services/agents/tests/test_investigation_completeness.py
pytest services/agents/tests/test_response_quality.py
The harness writes eval_report.json and eval_mitre_accuracy_report.json,
which the public eval harness page renders.
The same harness runs in CI on every PR — see
.github/workflows/ci.yml. PRs that regress any
axis below the published numbers must include a written justification and
before/after delta in the PR body.
Be honest about what changed. The harness runs deterministic substrate code (extractors, fusion, templates, judges) against synthetic incidents — it does not call the live LLM agent. Three of the four metrics are substrate self-consistency gates rather than agent accuracy scores. The eval harness page explains exactly which is which. If your PR changes which metric category a suite belongs to, update that page in the same commit.
Submitting a Pull Request
- Update your branch:
git fetch upstream && git rebase upstream/main - Run tests:
pnpm --filter @aisoc/web test(web smoke tests)pytest services/<name>/tests/for any Python service you touched( cd services/<name> && go test ./... )for any Go service you touched
- Push to your fork:
git push origin feature/my-feature - Open a PR on GitHub against
mainwith a clear description of changes. CI also runs ondevelopfor integration branches; both targets are accepted, but most contributors should targetmain.
Adding New Connectors
Connectors are runtime data. There is no Dockerfile to build, no
docker-compose.yml entry to add, and no separate microservice to ship —
adding a connector means subclassing BaseConnector, declaring a schema(),
adding a marketplace manifest, and writing tests. The
connector platform doc explains the
end-to-end click-and-connect flow; this section covers the contributor
mechanics.
1. Subclass BaseConnector
Add a new file under services/connectors/app/connectors/<name>.py and
subclass BaseConnector from
services/connectors/app/connectors/base.py.
Implement four things:
from .base import BaseConnector, ConnectorSchema, Field, OAuthHints
class MyConnector(BaseConnector):
connector_category = "saas" # one of: edr, siem, cloud, iam, saas, vcs, network
@classmethod
def schema(cls) -> ConnectorSchema:
return ConnectorSchema(
name="my-connector",
label="My Connector",
description="What this connector pulls and from where.",
category=cls.connector_category,
fields=[
Field(name="api_url", type="text", label="API URL", required=True),
Field(name="api_token", type="secret", label="API token", required=True, secret=True),
],
oauth=OAuthHints(supported_in_hosted=False),
default_poll_interval_seconds=300,
)
async def test_connection(self) -> dict:
... # one cheap auth-checking call; return {"ok": bool, "message": str, ...}
async def fetch_alerts(self, since_seconds: int = 300) -> list[dict]:
... # raw vendor JSON, no normalization
def normalize(self, raw_event: dict) -> dict:
... # OCSF-aligned shape; severity ∈ {"info","low","medium","high"}
Field types are text, secret, select (with options), textarea,
and oauth. Mark anything sensitive with secret=True — the
frontend will render those fields as masked inputs and the API service
will encrypt them via the CredentialVault
before they hit Postgres.
2. Register in the connector registry
Add your class to the _CONNECTOR_CLASSES tuple and __all__ in
services/connectors/app/connectors/__init__.py.
The registry powers /connectors/schemas and the wizard's catalog grid —
no other wiring required.
3. Add a marketplace plugin manifest
Drop a plugins/<connector-id>/plugin.yaml mirroring your schema(). See
existing entries (plugins/azure-entra/plugin.yaml,
plugins/cloudflare/plugin.yaml, etc.) for the exact shape. Run:
pnpm marketplace:sync # builds marketplace/index.json + syncs to apps/web/public
4. Write tests
services/connectors/tests/test_<name>.py with at minimum:
- A schema-contract test (asserts
schema().name, required fields, secret markers, hosted-OAuth advertising). normalize()unit tests covering every severity rule you implement — use real attacker-shaped fixtures (BEC, role grants, defense evasion).- A
respx-mockedtest_connection()covering success + auth-failure paths. - A
respx-mockedfetch_alerts()roundtrip that asserts your normalization produces a well-formed OCSF event.
The bundled test suite already runs against 100+ tests across 9 connectors
— look at test_azure_connectors.py, test_gcp_connectors.py, and
test_saas_connectors.py for the shape.
5. Docs
Add apps/docs/docs/connectors/<connector-id>.md with prereqs, exact
permissions/scopes, secret rotation walkthrough, severity heuristics
table (mirrors what's in your normalize()), and a troubleshooting
section. Add the new page to apps/docs/sidebars.ts under the
Connectors category.
Reference connectors
The 14 in-tree connectors are ordered roughly from simplest auth to most
complex: crowdstrike, okta, cloudflare (API token), splunk,
aws_security_hub (IAM keys), azure_entra, azure_activity,
azure_defender, m365_audit (AAD app), gcp_cloud_audit, gcp_scc,
google_workspace (service account JSON + JWT signing), github
(fine-grained PAT), microsoft_sentinel. Pick the one whose auth model
matches yours and copy from there.
Community Marketplace
The AiSOC marketplace is content-as-code. Anything in
detections/, playbooks/, and
plugins/ is automatically picked up by
scripts/build_marketplace.py and surfaced in
the in-app Marketplace view at /marketplace. There is no separate
registry to push to — you ship a PR, the index regenerates, and your
contribution shows up.
Where contributions go
Each content type has a community/ namespace reserved for outside
contributors:
- Detections →
detections/community/<your-rule>.yaml - Playbooks →
playbooks/community/<pack-name>/<your-playbook>.playbook.json - Plugins →
plugins/community/<your-plugin-id>/
These show up in the Marketplace with a Community badge (versus the
Verified badge on AiSOC-authored content). Core content lives directly
under detections/<category>/, playbooks/packs/v1/<category>/, and
plugins/<plugin-id>/.
Submitting a contribution
- Pick the right namespace (rule, playbook, or plugin) and follow the schema
used by an existing item of the same type. Detection schema lives in
detections/README.md, playbook schema inplaybooks/README.md, plugin schema inpackages/plugin-sdk-py/README.mdandpackages/plugin-sdk-go/README.md. - Rebuild the marketplace index locally:
pnpm marketplace:build pnpm marketplace:sync - Verify CI will be happy:
pnpm marketplace:check # asserts the index matches what's on disk python3 scripts/validate_detections.py # if you added detections - Open a PR. CI runs
marketplace:check, detection validation, and any plugin SDK tests. A maintainer will review for content quality, MITRE ATT&CK accuracy, and false-positive notes.
Quality bar for community marketplace items
- Detections must include MITRE ATT&CK technique IDs in
tags(formatmitre.attack.T1234[.567]) and a fixture underdetections/fixtures/. - Playbooks must declare a clear trigger, an explicit decision tree, and any human-approval gates. No silent destructive actions.
- Plugins must implement the relevant SDK interface in either Python or
Go (preferably both). They must declare
min_aisoc_version,license, and ahomepageURL. Network calls go through the SDK's HTTP helpers, never barerequestsornet/httpcalls. - All items get
verified: falseandsource: "community"in the index until a maintainer promotes them.
Reporting Bugs
Please use the GitHub issue tracker. Include:
- AiSOC version
- OS and environment
- Steps to reproduce
- Expected vs actual behavior
- Relevant logs
License
By contributing, you agree that your contributions will be licensed under the MIT License.