Get started

Install a self-hosted security graph

Core runs as two processes that share one Postgres database: the om CLI (scans, rules, queries) and the API (console and HTTP). Docker Compose starts Postgres and the API. You build or download the CLI and point it at the published database port.

Prerequisites

  • Docker with Compose
  • Go 1.27.1 if you build om from source (or a release archive from GitHub Releases)
  • Cloud credentials only when you scan a real account. The demo graph needs none.

Start Postgres and the API

git clone https://github.com/OpenSourceOM/core.git
cd core
cp .env.example .env
docker compose up -d

Compose publishes Postgres on localhost:5432 and the API on http://localhost:8080. The API applies pending migrations before it listens.

Compose reads .env for the API container. The CLI does not. It defaults to POSTGRES_HOST=localhost, which matches the published port. Leave POSTGRES_HOST=postgres inside .env for the container, and do not export that value into the shell you use for om.

Build the CLI

go build -o om ./cmd/om

The API container runs om serve, which applies the SQL files in migrations/ before it listens. A later start applies only files that are not already recorded. When Compose is running Postgres without the API, run om migrate from the repository root. Set OM_AUTO_MIGRATE=false on the API when that command should be the only schema change.

Load the sample graph

./om scan demo
./om paths list
./om paths run internet-to-datastore
./om graph stats

om scan demo replaces the previous demo account, upserts a small AWS-and-Kubernetes sample, runs every CSPM rule, and prints one attack path.

What the demo shows

The command replaces account ids 111122223333 and cluster-demo, then prints an attack path and up to eight findings from those accounts. The path is Internet → sg-web → web-1 → AdminRole → prod-db. worker-1 has no public IP and only reaches the private bucket acme-assets through AppRole. acme-logs-public is a public bucket that is not on the web-1 path. The Kubernetes sample is a workload named frontend with k8s_service_type: LoadBalancer and image nginx:1.25.3, reachable from the internet and not connected to prod-db. web-1 lists package cpe:2.3:a:apache:log4j:2.14.1:*:*:*:*:*:*:*. worker-1 lists the same product at 2.17.1. om enrich cve attaches CVE-2021-44228 to web-1 because that package matches.

Open http://localhost:8080. The console opens on internet-to-datastore, which should draw that path. Stats should be non-zero, and the findings list should include attack-path rows for web-1 to prod-db, the public datastore, and the internet-exposed workload. web-1 matches the internet-exposed workload rule, the public-IP rule, and the IMDSv1 rule, so each of those becomes its own attack-path finding. The card lists the ids internet:global, aws:sg:sg-web, aws:ec2:i-web-1, aws:iam:role/AdminRole, and aws:rds:prod-db. If the page is empty, the CLI and the API are pointed at different databases. See Docker Compose.

Scan a cloud account

Collectors use each cloud’s default credential chain. They are read-only toward the account. Export variables in the shell, then scan. Details and IAM scope are on the collector pages.

export AWS_REGION=us-east-1
./om scan aws

export AZURE_SUBSCRIPTION_ID=00000000-0000-0000-0000-000000000000
./om scan azure

export GCP_PROJECT_ID=my-project
./om scan gcp

./om scan k8s

After a scan, evaluate rules and attach CVEs that match workload inventory:

./om rules run
./om enrich cve
./om identity blast-radius --name AdminRole

Next

Questions

How do I install OpenSourceOM?

Clone the OpenSourceOM/core repository and start Postgres and the API with Docker Compose. The API applies database migrations when it starts. Build the om CLI, then om scan demo loads a sample graph at http://localhost:8080.

Can I try OpenSourceOM without cloud credentials?

Yes. om scan demo loads a sample environment and runs the CSPM rules. AWS, Azure, GCP, and Kubernetes scans are separate commands and use each cloud’s default credential chain.

Related reading