Skip to main content

entrygraph

Query your codebase like a graph. entrygraph indexes a repository into a SQLite database (through the SQLAlchemy ORM) and answers questions about symbols, classes/methods, entrypoints (HTTP routes, CLI commands, main functions, tasks, lambda handlers), the call graph (callers, callees, references), and source → sink reachability ("can any HTTP route reach subprocess.run?").

Language-agnostic via tree-sitter; first-class support for Python, JavaScript/TypeScript, Go, Java, Ruby, C#, PHP, and Rust, with language and framework detection.

Entrypoints include decorator/attribute routes, call-based route registration, middleware, and config-file handlers (serverless, SAM, Procfile, Dockerfile). Reachability enumerates real call paths from a catalog taint source to a tagged sink and reports checkable facts about each — the sink's catalog severity, the weakest edge confidence, and a same-function reaching-defs verdict (flow: confirmed / not observed) — with class-hierarchy analysis to recover virtual dispatch. Every hop carries a file:line and the literal source and sink lines, so a finding is a lead you can open and verify, not a score to trust.

Install

pip install entrygraph        # or: uv pip install entrygraph

Requires Python ≥ 3.13. Installs the entrygraph command (you can also run it as uv run entrygraph … or python -m entrygraph …).

Quick start (CLI)

Index a repo once, then query it as often as you like:

cd /path/to/acme-api
entrygraph index .          # build the graph
entrygraph entrypoints      # query it — no --db needed

One global store, auto-scoped. By default every index writes into a single shared database at ~/.entrygraph/.entrygraph.db, keyed by repo root, and every query command scopes to the repository whose root is your working directory (or its nearest ancestor). So you index each repo once and just cd between them — no per-project file to track. To query a repo from outside its directory, pass --repo <path-or-name> (e.g. --repo acme-api); entrygraph repos lists every indexed repository and the names you can pass. --db PATH points any command at an isolated database instead (handy in CI), and every query command takes --json for machine-readable output.

$ entrygraph repos
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━┓
┃NAME      ┃ ROOT                  ┃ FILES ┃ SYMBOLS┃
┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━┩
│acme-api  │ /Users/you/acme-api   │    42 │    611 │
│acme-web  │ /Users/you/acme-web   │   118 │   1930 │
└──────────┴───────────────────────┴───────┴────────┘

index — build the graph

Walk the tree and extract symbols, imports, and calls into the index. Incremental by default (only changed files are reparsed); --full rebuilds, --paranoid re-hashes every file (skips the mtime fast path), and --include-tests indexes test files too (excluded by default; flipping it needs --full).

entrygraph index .
╭───────────────── ✓ indexed acme-api ──────────────────╮
│ files    5 indexed, 0 skipped, 0 deleted of 5 scanned │
│ graph    32 symbols  34 edges  5 entrypoints          │
│ db       /Users/you/.entrygraph/.entrygraph.db        │
╰─────────────────────── 0.137s ────────────────────────╯

The positional argument may also be a git URL — entrygraph clones it and indexes the checkout:

entrygraph index https://github.com/semgrep/semgrep     # or git@github.com:org/repo.git

The clone lands in a reused workspace (./.entrygraph/clones/<host>/<org>/<repo>) and the graph goes into the same global index (keyed by the checkout root), so follow-up queries run from that checkout directory or with --db. Re-running index <url> fetches and updates the existing checkout instead of re-cloning. The clone is hardened — shallow, repo hooks disabled, no interactive credential prompt, and a wall-clock timeout — and the indexed code is never executed.

URL flag Meaning
--ref REF branch, tag, or commit to check out (default: remote HEAD)
--depth N / --full-clone clone depth (default 1; --full-clone = full history)
--clone-dir DIR where to place the checkout
--ephemeral clone to a temp dir and delete it after indexing (no paths snippets afterward)
--timeout SECONDS max clone/fetch wall-time (default 600)

Private repos work when the ambient git environment already authenticates (SSH agent, credential helper, or a token in the URL); entrygraph never prompts for or stores secrets.

detect — languages & frameworks

Byte-share per language plus framework detections scored from manifest dependencies and code signals.

entrygraph detect
Languages
┏━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
┃LANGUAGE ┃ FILES ┃ SHARE              ┃
┡━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
│python   │     5 │ ████████████ 100.0%│
└─────────┴───────┴────────────────────┘
Frameworks
┏━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━┓
┃FRAMEWORK ┃ LANGUAGE ┃ CONFIDENCE     ┃
┡━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━┩
│flask     │ python   │ █████████░ 0.94│
│click     │ python   │ █████████░ 0.94│
└──────────┴──────────┴────────────────┘

entrypoints — your attack surface

Every HTTP route, CLI command, task, lambda, middleware, and main — with its framework, method, route, and handler symbol, grouped by kind, framework, and route. Filter with --kind, --framework, or --route, and cap with --limit.

entrypoints --kind http_route      # or: --framework flask / --route '/api/*'
┏━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃KIND        ┃ FRAMEWORK ┃ METHOD   ┃ ROUTE            ┃ HANDLER                 ┃
┡━━━━━━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━┩
│http_route  │ flask     │ GET      │ /users/<user_id> │ app.routes.get_user     │
│http_route  │ flask     │ GET      │ /health          │ app.routes.health       │
│http_route  │ flask     │ GET,POST │ /reports         │ app.routes.create_report│
│cli_command │ click     │          │                  │ cli.report              │
│main        │           │          │                  │ cli                     │
└────────────┴───────────┴──────────┴──────────────────┴─────────────────────────┘
5 entrypoint(s)

symbols, callers, callees, references — search & walk the call graph

symbols globs on --name or --qname (filter by --kind/--file, cap with --limit):

entrygraph symbols --kind class --name 'Report*'
┏━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━┳━━━━━┓
┃KIND  ┃ QNAME                     ┃ FILE            ┃ LINE┃
┡━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━╇━━━━━┩
│class │ app.services.ReportRunner │ app/services.py │   10│
└──────┴───────────────────────────┴─────────────────┴─────┘

callers/callees walk the call graph (--depth N, default 1) and list the distinct symbols on the other end of an edge:

entrygraph callers app.services.run_report        # who calls it
entrygraph callees app.services.run_report        # what it calls
┏━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃KIND     ┃ QNAME                    ┃ FILE         ┃
┡━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━┩
│function │ app.routes.create_report │ app/routes.py│
│function │ cli.report               │ cli.py       │
└─────────┴──────────────────────────┴──────────────┘

By default callers/callees list only resolved edges (exact/import and unique-name fuzzy binds). --include-speculative adds class-hierarchy guesses and unresolved wildcard/dynamic calls (lower confidence, noisier).

references is the drill-down: instead of distinct caller symbols, it lists every individual call site targeting a symbol, each with its file:line and edge-resolution confidence — the checkable form you act on:

entrygraph references app.services.run_report
┏━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃CALLER                   ┃ LOCATION         ┃ CONFIDENCE┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│app.routes.create_report │ app/routes.py:20 │ import    │
│cli.report               │ cli.py:11        │ import    │
└─────────────────────────┴──────────────────┴───────────┘

paths — source → sink reachability

Can anything reach a dangerous sink? Paths are drawn as call cards, ordered by their facts — confirmed flows first, then by sink severity, then by the weakest edge confidence. Each hop shows its resolution confidence (exact/import/fuzzy/unresolved) and its file:line; the sink node is flagged (), and the literal source and sink lines are printed so you can read the actual call.

entrygraph paths --source-category http_input --sink-category command_exec
1 path(s)  category:http_input → category:command_exec

[1] severity high  confidence import
  source  create_report   app/routes.py:12  (http_input · explicit · query "cmd")
          cmd = request.args.get("cmd")
    ↓     run_report      app/routes.py:20  import
    ↓     start           app/services.py:27  fuzzy
  sink    subprocess.run  app/services.py:22  ⚡ py.command-exec.subprocess  import
          subprocess.run(cmd, shell=True)
       flow: confirmed (2 hops)
  • Reachability check: paths exits 0 when a path is found, 1 when none — entrygraph paths --source-category http_input --sink-category command_exec && echo reachable.

  • Sources & sinks: use --source-category/--sink-category to start from every registered taint source and end at every tagged sink of a category, or name an exact --source/--sink (the language prefix is optional — --sink subprocess.run resolves to py:subprocess.run). Combine a --source glob with a category to union both. paths --list-categories prints the valid category names for the index; an unknown category is a hard error, never a silent empty result:

    entrygraph paths --list-categories
    # source categories  cli_arg, env_input, http_input, stdin_input, user_input
    # sink categories    code_eval, command_exec, deserialization, path_traversal, sql, ssrf, …
    
  • Precision/recall dial: by default the search is adaptive — it tries only high-confidence (resolved) edges first and automatically widens to the speculative frontier if that finds nothing. --strict disables the widening (resolved edges only). To force a specific frontier for one run: widen with --include-unresolved (wildcard py:*.execute sinks + dynamic calls), --include-fuzzy (speculative class-hierarchy edges), or --include-callbacks (function/method values passed as arguments — handler registrations like http.HandleFunc("/", handler) or this::handle); --min-confidence N sets an explicit floor. Bound the search with --max-depth (default 25) and --max-paths (default 10).

  • Source provenance: an http_input/cli_arg source is labeled · explicit when the handler demonstrably reads request input (a catalog accessor call like request.args.get("q")) or · handler when the handler is merely shaped like a source and reaches the sink without a proven read. --explicit-sources drops the handler-only seeds entirely (at the cost of property-read frameworks like Express req.body).

  • Flow verification: a bounded reaching-defs check labels each path with whether a source value actually flows to the sink — flow: confirmed when it does, flow: not observed when it provably doesn't. It follows up to --taint-hops interior call hops (default 5; 0 = same-function only) and is conservative: anything it can't analyze stays unlabeled. --confirmed-only keeps just the confirmed paths.

What a finding means. A path is a reachability lead to triage, not a confirmed dataflow: it says a source-bearing symbol can reach a sink-bearing symbol through the call graph. Every field is a checkable fact — open the file:lines and read the code. The severity is the tagged sink's catalog severity; the per-hop confidence tags (exact/fuzzy/unresolved) are edge-resolution confidence, not taint confidence; the flow: label is the reaching-defs verdict where the check can see the code. There is no blended "risk" number to trust — the ordering surfaces confirmed, higher-severity, better-resolved paths first so you triage the list top-down.

stats & --json

entrygraph stats
entrygraph --help          # every command and flag
╭──────────────── index stats ────────────────╮
│ ┏━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┓ │
│ ┃metric           ┃                 value┃ │
│ ┡━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━┩ │
│ │repo_root        │ /path/to/acme-api    │ │
│ │index_generation │                     1│ │
│ │files            │                     5│ │
│ │symbols          │                    32│ │
│ │edges            │                    34│ │
│ │resolved_edges   │                    34│ │
│ │entrypoints      │                     5│ │
│ │sink_edges       │                     2│ │
│ │source_edges     │                     1│ │
│ └─────────────────┴──────────────────────┘ │
╰──────────────────────────────────────────────╯
taint catalog: python full

Add --json to any query command for machine-readable output. Each path carries its severity, min_confidence (the weakest edge confidence, 0 unresolved → 3 exact), taint_verified (the flow verdict), source provenance, the symbol chain, and the literal source/sink lines:

[
  {
    "length": 5,
    "min_confidence": 2,
    "severity": "high",
    "may_continue": false,
    "source_kind": "explicit",
    "taint_verified": true,
    "source_channel": "query",
    "source_key": "name",
    "symbols": [
      "app.routes.create_report", "app.services.run_report",
      "app.services.ReportRunner.start",
      "app.services.ReportRunner.render_and_execute", "py:subprocess.run"
    ],
    "lines": [20, 27, 17, 22],
    "source_line": "cmd = request.args.get(\"cmd\")",
    "sink_line": "subprocess.run(cmd, shell=True)"
  }
]

Colored tables, share/confidence bars, and severity highlighting render in a real terminal; piped or --json output is plain text.

serve — a web UI over the index

To browse an index visually rather than via the CLI, entrygraph serve runs a web app that walks a repo's symbols, entrypoints/routes, callers/callees, source→sink paths, and an interactive call graph, and can register and index repositories from the UI:

entrygraph index . --db /tmp/graph.db
entrygraph serve --db /tmp/graph.db           # http://127.0.0.1:8100

It supports Authentik SSO (EG_OIDC_*), with a zero-setup local mode (no auth) on loopback by default, and ships behind the entrygraph[server] extra. Build the UI once with cd webapp && npm run build; the build lands in the package and is served at /.

Python API

from entrygraph import CodeGraph

# Index a repo (creates <repo>/.entrygraph.db by default)
graph = CodeGraph.index("/path/to/repo")

# ...or open an existing index
graph = CodeGraph.open("graph.db")

# Symbols — glob on name or qualified name, filter by kind or file
graph.symbols(kind="class", name="User*")
graph.symbol("app.services.Runner.execute")        # exact; raises if missing

# Detection
report = graph.detect()
report.languages      # -> [DetectedLanguage(name="python", percent=96.7, ...), ...]
report.frameworks     # -> [DetectedFramework(name="flask", confidence=0.94, ...), ...]

# Entrypoints
graph.entrypoints(framework="flask")
graph.entrypoints(kind="http_route", route="/api/*")

# Call graph
graph.callers("app.services.run_report")            # who calls it
graph.callees("app.services.run_report", depth=3)   # what it (transitively) calls
graph.references("app.models.CONST")                # inbound edges of any kind

# Source -> sink reachability (ordered by facts: confirmed flows, severity, confidence)
paths = graph.paths(source="app.routes.*", sink_category="command_exec")
for p in paths:
    print(p.severity, p.taint_verified, p.render(), "(+may continue)" if p.may_continue else "")
    # high True  app.routes.create_report -> app.services.run_report (line 20)
    #   -> ...ReportRunner.render_and_execute (line 17) -> py:subprocess.run (line 22)

graph.reachable(source="app.routes.upload", sink="py:subprocess.run")   # -> bool

# Valid category names (an unknown category raises UnknownCategoryError)
graph.sink_categories()      # -> ["command_exec", "sql", "path_traversal", ...]
graph.source_categories()    # -> ["http_input", "cli_arg", "env_input", ...]

# Precision/recall dial. By default only EXACT/IMPORT and unique-name FUZZY
# edges are traversed. Opt into wider (noisier) traversal:
graph.paths(source="app.routes.*", sink_category="sql",
            include_unresolved=True)   # follow py:*.execute wildcard-sink guesses
graph.paths(source="app.routes.*", sink_category="command_exec",
            include_fuzzy=True)        # follow speculative class-hierarchy (CHA) edges
graph.paths(source="app.routes.*", sink_category="command_exec",
            confirmed_only=True)       # keep only paths where a flow is confirmed

# Incremental re-index (only changed/added/deleted files are reparsed)
graph.refresh()

# Escape hatches
graph.session()               # raw SQLAlchemy Session
graph.sql("SELECT ...")       # textual query -> list[dict]

Every result is a frozen, immutable dataclass detached from the DB session, so results are safe to hold and trivial to serialize.

How it works

  1. Walkos.scandir with hard-pruned junk dirs (node_modules, .venv, …), .gitignore rules, and size/binary/minified gates. Every skip is recorded with a reason.
  2. Extract — tree-sitter .scm queries harvest definitions/imports/calls; small per-language "shaper" modules build qualified names, import maps, and receiver info. Parsing runs across a process pool for large repos.
  3. Resolve — a two-pass resolver binds references to symbols with a confidence level (exact / import / fuzzy / unresolved). External callees (subprocess.run, child_process.exec, …) become placeholder nodes so sinks are real graph terminals.
  4. Detect — frameworks are scored from manifest dependencies plus code signals (noisy-or); entrypoint rules map framework patterns to route/command records.
  5. Store — everything persists to SQLite via the SQLAlchemy 2.0 ORM with bulk inserts and app-assigned keys. Re-indexing is incremental and content-hash driven.
  6. Query — reachability runs over an in-memory adjacency cache (BFS/DFS with cycle handling); a recursive-CTE SQL engine is available as a fallback (engine="sql").

Extending

  • Custom sinks/sources — drop an entrygraph.toml in the repo root with [[sink]] / [[source]] tables (same schema as the built-in data/sinks/*.toml), or call entrygraph.detect.taint.register_sink(...) / register_source(...). Third-party wrapper libraries that reach a sink internally are covered by data/sinks/lib_*.toml "library summaries" (same schema, with a library = "..." tag).
  • New frameworks / entrypoints — register a FrameworkSpec and an EntrypointRule; adding a framework is usually a few lines.
  • New languages — add a <lang>/{definitions,imports,calls}.scm query set and a shaper implementing the LanguageExtractor protocol.

Releasing

Merging to main auto-bumps the patch version (via a git tag) and publishes to PyPI through Trusted Publishing — see RELEASING.md. The package version is derived from git tags by hatch-vcs, so it's never hand-edited.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

entrygraph-0.1.123.tar.gz (437.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

entrygraph-0.1.123-py3-none-any.whl (263.7 kB view details)

Uploaded Python 3

File details

Details for the file entrygraph-0.1.123.tar.gz.

File metadata

  • Download URL: entrygraph-0.1.123.tar.gz
  • Upload date:
  • Size: 437.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for entrygraph-0.1.123.tar.gz
Algorithm Hash digest
SHA256 ec16c4710d95d9a8fd027fd7ee8bcd92173b6a6a3e3943deea9cefbea8de836b
MD5 6d896f6eda32dccda4423918443bf0ef
BLAKE2b-256 3c2fe72323708db53f4fa078b9a41b041df60e20fac6170588032c1cc744c362

See more details on using hashes here.

Provenance

The following attestation bundles were made for entrygraph-0.1.123.tar.gz:

Publisher: release.yml on brettbergin/entrygraph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file entrygraph-0.1.123-py3-none-any.whl.

File metadata

  • Download URL: entrygraph-0.1.123-py3-none-any.whl
  • Upload date:
  • Size: 263.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for entrygraph-0.1.123-py3-none-any.whl
Algorithm Hash digest
SHA256 e0be33df35706feecacca7f33f98d076fad882fc7a7360096f2e7148cd5e8eef
MD5 f482aa24dcf83b25bd7d060cb7e8e711
BLAKE2b-256 1a9c77a68ba53df74ff3d295d07465ae916f6f95bc480f89fae2af33c93653ab

See more details on using hashes here.

Provenance

The following attestation bundles were made for entrygraph-0.1.123-py3-none-any.whl:

Publisher: release.yml on brettbergin/entrygraph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.1.134

2 files

0.1.133

2 files

0.1.132

2 files

0.1.131

2 files

0.1.130

2 files

0.1.129

2 files

0.1.128

2 files

0.1.127

2 files

0.1.126

2 files

0.1.125

2 files

0.1.124

2 files

This release

0.1.123 This release

2 files

0.1.122

2 files

0.1.121

2 files

0.1.120

2 files

0.1.119

2 files

0.1.118

2 files

0.1.117

2 files

0.1.116

2 files

0.1.115

2 files

0.1.114

2 files

0.1.113

2 files

0.1.112

2 files

0.1.111

2 files

0.1.110

2 files

0.1.109

2 files

0.1.108

2 files

0.1.107

2 files

0.1.106

2 files

0.1.105

2 files

0.1.104

2 files

0.1.103

2 files

0.1.102

2 files

0.1.101

2 files

0.1.100

2 files

0.1.99

2 files

0.1.98

2 files

0.1.97

2 files

0.1.96

2 files

0.1.95

2 files

0.1.94

2 files

0.1.93

2 files

0.1.92

2 files

0.1.91

2 files

0.1.90

2 files

0.1.89

2 files

0.1.88

2 files

0.1.87

2 files

0.1.86

2 files

0.1.85

2 files

0.1.84

2 files

0.1.83

2 files

0.1.82

2 files

0.1.81

2 files

0.1.80

2 files

0.1.79

2 files

0.1.78

2 files

0.1.77

2 files

0.1.76

2 files

0.1.75

2 files

0.1.74

2 files

0.1.73

2 files

0.1.72

2 files

0.1.71

2 files

0.1.70

2 files

0.1.69

2 files

0.1.68

2 files

0.1.67

2 files

0.1.66

2 files

0.1.65

2 files

0.1.64

2 files

0.1.63

2 files

0.1.62

2 files

0.1.61

2 files

0.1.60

2 files

0.1.59

2 files

0.1.58

2 files

0.1.57

2 files

0.1.56

2 files

0.1.55

2 files

0.1.54

2 files

0.1.53

2 files

0.1.52

2 files

0.1.51

2 files

0.1.50

2 files

0.1.49

2 files

0.1.48

2 files

0.1.47

2 files

0.1.46

2 files

0.1.45

2 files

0.1.44

2 files

0.1.43

2 files

0.1.42

2 files

0.1.41

2 files

0.1.40

2 files

0.1.39

2 files

0.1.38

2 files

0.1.37

2 files

0.1.36

2 files

0.1.35

2 files

0.1.34

2 files

0.1.33

2 files

0.1.32

2 files

0.1.31

2 files

0.1.30

2 files

0.1.29

2 files

0.1.28

2 files

0.1.27

2 files

0.1.26

2 files

0.1.25

2 files

0.1.24

2 files

0.1.23

2 files

0.1.22

2 files

0.1.21

2 files

0.1.20

2 files

0.1.19

2 files

0.1.18

2 files

0.1.17

2 files

0.1.16

2 files

0.1.15

2 files

0.1.14

2 files

0.1.13

2 files

0.1.12

2 files

0.1.11

2 files

0.1.10

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.5

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page