{
 "info": {
  "name": "G-Log API",
  "description": "G-Log · Garia.Net — every endpoint the tool's own page uses, so anything the page can do a script can do too.\n\nSet the collection variables first: `baseUrl` and, outside this machine, `key`.\n\n- **The page on this machine** (`glog --open` or `glog --server` without `--network`) listens on `http://127.0.0.1:7668` and asks for no key.\n- **A server or an agent on the network** listens on `7788` over **HTTPS with its own certificate**. Postman will not trust that certificate: turn off *Settings → General → SSL certificate verification*, or pin it yourself. Read the fingerprint with `GET /api/hello`, and the key on that machine with `glog --show-key`.\n\nStart with **hello**: it says which role this is, whether a key is required, and the certificate fingerprint. Every call that needs one takes the key in `X-GLog-Token`; a GET may also use `?token=`, which is handy in a browser but ends up in your shell history.\n\nThe report is the same JSON everywhere - page, command line and MCP - and `api/API.md` describes every chapter, analysis and advice code.",
  "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
 },
 "variable": [
  {
   "key": "baseUrl",
   "value": "http://127.0.0.1:7668",
   "description": "The page on this machine. A server or an agent on the network: https://logserver:7788"
  },
  {
   "key": "key",
   "value": "",
   "description": "The key of that machine. Empty on the loopback, which asks for none. Read one with: glog --show-key"
  },
  {
   "key": "path",
   "value": "C:\\inetpub\\logs\\LogFiles\\W3SVC1",
   "description": "A log folder, a file, a .gz, a .zip or an ssh:// address on the machine that answers"
  },
  {
   "key": "site",
   "value": "Shop",
   "description": "A site of this cluster, by its name in applicationHost.config. On a server this replaces a path"
  },
  {
   "key": "day",
   "value": "2026-09-17",
   "description": "A day for the PCI review, in UTC"
  },
  {
   "key": "schedule",
   "value": "shop-daily",
   "description": "The name of a report in reports.json"
  },
  {
   "key": "file",
   "value": "u_ex260917.log",
   "description": "A log file name. Take one from /api/agent/summaries - which files an agent has is up to its rollover"
  }
 ],
 "auth": {
  "type": "apikey",
  "apikey": [
   { "key": "key", "value": "X-GLog-Token", "type": "string" },
   { "key": "value", "value": "{{key}}", "type": "string" },
   { "key": "in", "value": "header", "type": "string" }
  ]
 },
 "item": [
  {
   "name": "1 · Is anyone there",
   "item": [
    {
     "name": "hello · who is listening",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/api/hello", "host": ["{{baseUrl}}"], "path": ["api", "hello"] },
      "description": "The only call that never needs a key: the tool, its version, the role (page, agent or server), whether a key is required, and the fingerprint of the TLS certificate. Use it to check you are on the right port before anything else."
     }
    }
   ]
  },
  {
   "name": "2 · The report",
   "item": [
    {
     "name": "report · a folder or a file",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/report?path={{path}}&since=24h",
       "host": ["{{baseUrl}}"],
       "path": ["api", "report"],
       "query": [
        { "key": "path", "value": "{{path}}", "description": "A folder, a file, a .gz, a .zip, or an ssh:// address" },
        { "key": "since", "value": "24h", "description": "Only the lines of this window. Or from= and to= with a time, a date, or both" },
        { "key": "paths", "value": "C:\\Windows\\System32\\LogFiles\\HTTPERR", "description": "More sources at once, separated by a semicolon", "disabled": true },
        { "key": "compare", "value": "", "description": "A second period to put next to it: what got slower, which errors are new", "disabled": true },
        { "key": "top", "value": "10", "description": "How many rows in every ranked list", "disabled": true }
       ]
      },
      "description": "The whole report as JSON: every chapter with its analyses, the five questions, and the advice. This is the same JSON the page draws itself from and the same one `glog.com --report --format json` prints."
     }
    },
    {
     "name": "report · a site of the cluster",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/report?site={{site}}&since=24h",
       "host": ["{{baseUrl}}"],
       "path": ["api", "report"],
       "query": [
        { "key": "site", "value": "{{site}}", "description": "A site this server collects, over every machine that runs it" },
        { "key": "since", "value": "24h" },
        { "key": "machine", "value": "WEB01", "description": "Only this machine of that site", "disabled": true }
       ]
      },
      "description": "On a server: the same report, but built from the summaries of every machine that runs that site. Sites are matched by name, not by W3SVC number, because that number differs per node."
     }
    },
    {
     "name": "advice · only the findings",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/advice?path={{path}}&since=24h",
       "host": ["{{baseUrl}}"],
       "path": ["api", "advice"],
       "query": [{ "key": "path", "value": "{{path}}" }, { "key": "since", "value": "24h" }]
      },
      "description": "The findings on their own, most severe first, with the numbers behind them. Smaller than the whole report when all you want is 'is something wrong'."
     }
    },
    {
     "name": "overview · the five questions",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/overview?path={{path}}&since=24h",
       "host": ["{{baseUrl}}"],
       "path": ["api", "overview"],
       "query": [{ "key": "path", "value": "{{path}}" }, { "key": "since", "value": "24h" }]
      },
      "description": "Is something wrong, where, since when, what changed, who notices - plus the key numbers: requests, error shares, response time percentiles."
     }
    },
    {
     "name": "inventory · what is in the log",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/inventory?path={{path}}",
       "host": ["{{baseUrl}}"],
       "path": ["api", "inventory"],
       "query": [{ "key": "path", "value": "{{path}}" }]
      },
      "description": "Chapter 0: which files were read, which fields are logged and which are missing, the period, the format, and what cannot be answered because a field is off - with how to turn it on."
     }
    },
    {
     "name": "summary · the analysis as bytes",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/summary?path={{path}}",
       "host": ["{{baseUrl}}"],
       "path": ["api", "summary"],
       "query": [{ "key": "path", "value": "{{path}}" }]
      },
      "description": "The analysis in G-Log's own compact format - a few hundred kilobytes instead of gigabytes. This is what an agent sends to a server; `glog --report` reads it back."
     }
    }
   ]
  },
  {
   "name": "3 · Search",
   "item": [
    {
     "name": "search · the requests themselves",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/search?path={{path}}&query=status:5xx since:24h&limit=50",
       "host": ["{{baseUrl}}"],
       "path": ["api", "search"],
       "query": [
        { "key": "path", "value": "{{path}}" },
        { "key": "query", "value": "status:5xx since:24h", "description": "The query language: status, url, client, agent, referer, user, method, ms, bytes, text, and from/to/since. Combine with spaces, negate with -" },
        { "key": "limit", "value": "50", "description": "1 to 500 lines per page" },
        { "key": "skip", "value": "0", "description": "Use nextSkip from the answer to page on", "disabled": true },
        { "key": "site", "value": "{{site}}", "description": "On a server: ask the machines behind that site instead of a path", "disabled": true }
       ]
      },
      "description": "The matching log lines, with file, line number, every field by name, and the raw line. On a server with `site=`, the question goes to the agents and each line says which machine it came from - the lines themselves never leave the web server until you ask for them."
     }
    }
   ]
  },
  {
   "name": "4 · The cluster",
   "item": [
    {
     "name": "cluster/sites · which sites arrived",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/api/cluster/sites", "host": ["{{baseUrl}}"], "path": ["api", "cluster", "sites"] },
      "description": "Every site this server collects, with the machines that run it, how many summaries there are and how far they were read."
     }
    },
    {
     "name": "agents · are they all still there",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/api/agents", "host": ["{{baseUrl}}"], "path": ["api", "agents"] },
      "description": "Per agent: the machine, its version, whether it is fetched or sends by itself, the last contact, the clock difference, and the last error if there was one."
     }
    },
    {
     "name": "alerts · what is firing",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/api/alerts", "host": ["{{baseUrl}}"], "path": ["api", "alerts"] },
      "description": "Alerts that are firing and the ones that resolved in the last day, each with the site, since when, the numbers and a link to the view it is about. Sites whose baseline is still learning are named as such."
     }
    },
    {
     "name": "agent/status · on an agent",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/api/agent/status", "host": ["{{baseUrl}}"], "path": ["api", "agent", "status"] },
      "description": "Ask an agent directly (port 7788 on the web server): its version, time, sites, the last round, and - when they are on - what it forwarded to a SIEM and what ETW delivered."
     }
    },
    {
     "name": "agent/summaries · what it has ready",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/api/agent/summaries", "host": ["{{baseUrl}}"], "path": ["api", "agent", "summaries"] },
      "description": "The summaries an agent holds, with site, file, size and how far the log was read. A server uses this to see what it still needs."
     }
    },
    {
     "name": "agent/summary · fetch one",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/agent/summary?site={{site}}&file={{file}}",
       "host": ["{{baseUrl}}"],
       "path": ["api", "agent", "summary"],
       "query": [{ "key": "site", "value": "{{site}}" }, { "key": "file", "value": "{{file}}" }]
      },
      "description": "One summary as bytes, with the time it was read up to in X-GLog-Summary-Modified. This is the call a server makes every round. Run **agent/summaries** first and take a file name from it: which files an agent holds depends on its rollover."
     }
    },
    {
     "name": "agent/context · what happened on the server",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/agent/context",
       "host": ["{{baseUrl}}"],
       "path": ["api", "agent", "context"],
       "query": [{ "key": "day", "value": "2026-09-17", "description": "One day in full. Without it you get the list of days that are kept", "disabled": true }]
      },
      "description": "App pool crashes and recycles, deploys, certificates with their expiry, worker processes, processor and free disk - the things that explain what the log shows. Without `day` you get which days are there; with a day, that day in full."
     }
    }
   ]
  },
  {
   "name": "5 · Pairing and delivering",
   "item": [
    {
     "name": "pair · trade a one-time key for your own",
     "request": {
      "method": "POST",
      "header": [
       { "key": "Content-Type", "value": "application/json" },
       { "key": "X-GLog-Pair", "value": "ABCD-EFGH-JKLM", "description": "The one-time key from: glog --pair-key" }
      ],
      "body": {
       "mode": "raw",
       "raw": "{\n  \"machine\": \"WEB01\",\n  \"address\": \"web01:7788\",\n  \"thumbprint\": \"\"\n}"
      },
      "url": { "raw": "{{baseUrl}}/api/pair", "host": ["{{baseUrl}}"], "path": ["api", "pair"] },
      "description": "What an agent does once, so the key that opens the whole cluster is never typed on a web server. The answer holds a key of its own and the server's certificate fingerprint. The one-time key is gone afterwards; using it twice gives 401."
     }
    },
    {
     "name": "ingest · an agent delivers a summary",
     "request": {
      "method": "POST",
      "header": [{ "key": "Content-Type", "value": "application/octet-stream" }],
      "body": { "mode": "file", "file": { "src": "" } },
      "url": {
       "raw": "{{baseUrl}}/api/ingest?modified=1789600000",
       "host": ["{{baseUrl}}"],
       "path": ["api", "ingest"],
       "query": [{ "key": "modified", "value": "1789600000", "description": "How far the log was read, in UTC seconds" }]
      },
      "description": "The other direction: an agent that pushes sends the bytes of a summary here. Pick a file from /api/summary to try it. A paired agent may use its own key for this call, and only for this kind of call."
     }
    },
    {
     "name": "agent/context · an agent delivers what happened",
     "request": {
      "method": "POST",
      "header": [{ "key": "Content-Type", "value": "application/json" }],
      "body": {
       "mode": "raw",
       "raw": "{\n  \"machine\": \"WEB01\",\n  \"day\": \"2026-09-17\",\n  \"events\": [],\n  \"deploys\": [],\n  \"certificates\": []\n}"
      },
      "url": { "raw": "{{baseUrl}}/api/agent/context", "host": ["{{baseUrl}}"], "path": ["api", "agent", "context"] },
      "description": "The push side of the server context: an agent that sends by itself delivers the app pool crashes, deploys and certificates of a day here. Fetch the real shape with GET /api/agent/context on an agent first. `--no-context` on the agent turns the whole thing off."
     }
    },
    {
     "name": "agent/heartbeat · I am still here",
     "request": {
      "method": "POST",
      "header": [{ "key": "Content-Type", "value": "application/json" }],
      "body": { "mode": "raw", "raw": "{\n  \"machine\": \"WEB01\",\n  \"version\": \"1.0.0\",\n  \"intervalSeconds\": 60\n}" },
      "url": { "raw": "{{baseUrl}}/api/agent/heartbeat", "host": ["{{baseUrl}}"], "path": ["api", "agent", "heartbeat"] },
      "description": "An agent that sends by itself says it is alive, so a server can tell 'nothing happened' from 'this machine went quiet' - which is an alert of its own."
     }
    }
   ]
  },
  {
   "name": "6 · Reports on a schedule",
   "item": [
    {
     "name": "reports · what stands in reports.json",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/api/reports", "host": ["{{baseUrl}}"], "path": ["api", "reports"] },
      "description": "The schedules with their last and next run, and per schedule the files that were made, newest first."
     }
    },
    {
     "name": "reports/file · open one that was made",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/reports/file?name={{schedule}}&file={{schedule}}-20260917.html",
       "host": ["{{baseUrl}}"],
       "path": ["api", "reports", "file"],
       "query": [{ "key": "name", "value": "{{schedule}}" }, { "key": "file", "value": "{{schedule}}-20260917.html" }]
      },
      "description": "One generated report, as the file it is. Only a file that belongs to that schedule: a name with a path separator or .. is refused."
     }
    }
   ]
  },
  {
   "name": "7 · PCI DSS requirement 10",
   "item": [
    {
     "name": "reviews · the last thirty days",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/reviews?days=30",
       "host": ["{{baseUrl}}"],
       "path": ["api", "reviews"],
       "query": [{ "key": "days", "value": "30" }]
      },
      "description": "Per day: how many findings, how many still open, and who signed it off - plus how far the history reaches against the three months that must be immediately available and the twelve that must be kept."
     }
    },
    {
     "name": "review · one day in full",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/review?day={{day}}",
       "host": ["{{baseUrl}}"],
       "path": ["api", "review"],
       "query": [{ "key": "day", "value": "{{day}}" }, { "key": "site", "value": "{{site}}", "disabled": true }]
      },
      "description": "The findings of that day with their PCI reference, the status of each one and its history, and the sign-offs."
     }
    },
    {
     "name": "review · sign the day off",
     "request": {
      "method": "POST",
      "header": [{ "key": "Content-Type", "value": "application/json" }],
      "body": { "mode": "raw", "raw": "{\n  \"who\": \"Your Name\",\n  \"note\": \"daily review done\"\n}" },
      "url": {
       "raw": "{{baseUrl}}/api/review?day={{day}}",
       "host": ["{{baseUrl}}"],
       "path": ["api", "review"],
       "query": [{ "key": "day", "value": "{{day}}" }]
      },
      "description": "A name is not an account: G-Log has no users, so what is recorded is the name you send plus the Windows account the server runs under. Nothing is overwritten - every sign-off is added."
     }
    },
    {
     "name": "review · move one finding",
     "request": {
      "method": "POST",
      "header": [{ "key": "Content-Type", "value": "application/json" }],
      "body": {
       "mode": "raw",
       "raw": "{\n  \"finding\": \"pci.cardNumbers\",\n  \"status\": \"accepted\",\n  \"who\": \"Your Name\",\n  \"note\": \"known test data\"\n}"
      },
      "url": {
       "raw": "{{baseUrl}}/api/review?day={{day}}",
       "host": ["{{baseUrl}}"],
       "path": ["api", "review"],
       "query": [{ "key": "day", "value": "{{day}}" }]
      },
      "description": "open, investigated, accepted or resolved. The change is added to that finding's history with who did it and why; making the review again leaves it where it is."
     }
    },
    {
     "name": "integrity · the sealed log files",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/api/integrity", "host": ["{{baseUrl}}"], "path": ["api", "integrity"] },
      "description": "Every log file that was finished, with its SHA-256, when it was sealed and when it was last checked. A file that changed afterwards is `changed`, one that disappeared is `missing`, and both stay in the register."
     }
    },
    {
     "name": "audit · who asked for what",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/audit?limit=200",
       "host": ["{{baseUrl}}"],
       "path": ["api", "audit"],
       "query": [{ "key": "limit", "value": "200", "description": "1 to 5000 lines, newest first" }]
      },
      "description": "The tool's own audit trail: time, address, which key, what was asked and the result - refused requests included. Asking for it is itself an entry. The key itself is never written."
     }
    }
   ]
  },
  {
   "name": "8 · Settings and monitoring",
   "item": [
    {
     "name": "settings · what this machine runs on",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/api/settings", "host": ["{{baseUrl}}"], "path": ["api", "settings"] },
      "description": "Version, role, port, TLS and its fingerprint, masking, the round, how long summaries are kept, and where the key file, the data folder, the schedules and the audit trail live. No keys - only where they are."
     }
    },
    {
     "name": "metrics · for Prometheus",
     "request": {
      "method": "GET",
      "header": [],
      "url": { "raw": "{{baseUrl}}/metrics", "host": ["{{baseUrl}}"], "path": ["metrics"] },
      "description": "The same numbers in the text format Prometheus scrapes, so a dashboard you already have can draw them."
     }
    },
    {
     "name": "explain · what does this code mean",
     "request": {
      "method": "GET",
      "header": [],
      "url": {
       "raw": "{{baseUrl}}/api/explain?code=404.13",
       "host": ["{{baseUrl}}"],
       "path": ["api", "explain"],
       "query": [
        { "key": "code", "value": "404.13", "description": "An HTTP status with an IIS substatus, a Win32 code or HRESULT, or an HTTP.sys reason such as Timer_MinBytesPerSecond" },
        { "key": "kind", "value": "status", "description": "status, win32 or httperr - settles a number that could be either", "disabled": true }
       ]
      },
      "description": "What a code from a log means and what to check. The same explanations the page shows as a tooltip and `glog --explain` prints."
     }
    }
   ]
  },
  {
   "name": "9 · An AI assistant (MCP over HTTP)",
   "item": [
    {
     "name": "mcp · initialize",
     "request": {
      "method": "POST",
      "header": [
       { "key": "Content-Type", "value": "application/json" },
       { "key": "Accept", "value": "application/json, text/event-stream" }
      ],
      "body": {
       "mode": "raw",
       "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"method\": \"initialize\",\n  \"params\": {\n    \"protocolVersion\": \"2026-07-28\",\n    \"capabilities\": {},\n    \"clientInfo\": { \"name\": \"postman\", \"version\": \"1.0\" }\n  }\n}"
      },
      "url": { "raw": "{{baseUrl}}/mcp", "host": ["{{baseUrl}}"], "path": ["mcp"] },
      "description": "The same MCP server as `glog --mcp`, but over HTTP and behind the key, so an assistant on a workstation can ask about the whole cluster with nothing installed there."
     }
    },
    {
     "name": "mcp · tools/list",
     "request": {
      "method": "POST",
      "header": [
       { "key": "Content-Type", "value": "application/json" },
       { "key": "Accept", "value": "application/json, text/event-stream" }
      ],
      "body": { "mode": "raw", "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 2,\n  \"method\": \"tools/list\"\n}" },
      "url": { "raw": "{{baseUrl}}/mcp", "host": ["{{baseUrl}}"], "path": ["mcp"] },
      "description": "The nine tools with their JSON schema. On a server they take a site name instead of a path."
     }
    },
    {
     "name": "mcp · tools/call · glog_overview",
     "request": {
      "method": "POST",
      "header": [
       { "key": "Content-Type", "value": "application/json" },
       { "key": "Accept", "value": "application/json, text/event-stream" }
      ],
      "body": {
       "mode": "raw",
       "raw": "{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 3,\n  \"method\": \"tools/call\",\n  \"params\": {\n    \"name\": \"glog_overview\",\n    \"arguments\": { \"site\": \"{{site}}\", \"since\": \"24h\" }\n  }\n}"
      },
      "url": { "raw": "{{baseUrl}}/mcp", "host": ["{{baseUrl}}"], "path": ["mcp"] },
      "description": "The five questions and the key numbers, as an assistant would ask for them - the same answer as /api/overview, because it is the same report."
     }
    }
   ]
  }
 ]
}
