diff --git a/docs/abbreviations.md b/docs/abbreviations.md index d4af635..60f30aa 100644 --- a/docs/abbreviations.md +++ b/docs/abbreviations.md @@ -1,15 +1,16 @@ +*[API]: Application Programming Interface (a set of subroutine definitions, protocols, and tools for building application software) *[DNS]: Domain Name Service (decentralized naming system for computers, services, or other resources connected to the Internet) -*[FTL]: Pi-hole's Faster Than Light daemon +*[DnyDNS]: Dynamic DNS record pointing to a frequently changing IP address *[DHCP]: Dynamic Host Configuration Protocol (network management protocol for configuring Internet Protocol version 4 (IPv4) hosts with IP addresses) *[DHCPv6]: Dynamic Host Configuration Protocol version 6 (a network protocol for configuring Internet Protocol version 6 (IPv6) hosts with IP addresses) +*[FTL]: Pi-hole's Faster Than Light daemon *[IPv4]: Internet Protocol version 4 (addresses like 192.168.0.1) *[IPv6]: Internet Protocol version 6 (addresses like 2001:db8::ff00:42:8329) *[HTTP]: Hypertext Transfer Protocol (HTTP), an application protocol for distributed, collaborative, and hypermedia information systems *[HTTPS]: HTTP Secure (HTTPS), an extension of the Hypertext Transfer Protocol (HTTP) for secure communication over a computer network *[TCP]: Transmission Control Protocol (protocol providing reliable, ordered, and error-checked delivery of data between applications running on hosts communicating via an IP network) *[UDP]: User Datagram Protocol (a network communications method for sending messages as datagrams) -*[API]: Application Programming Interface (a set of subroutine definitions, protocols, and tools for building application software) *[PE]: Privacy Extension *[PID]: Process identifier (a number used to identify a process) *[HOSTS]: The computer file /etc/hosts is an operating system file that maps hostnames to IP addresses @@ -39,4 +40,6 @@ *[TFTP]: Trivial File Transfer Protocol is a simple lockstep File Transfer Protocol which allows a client to get a file from or put a file onto a remote host *[TTL]: Time-To-Live is a mechanism that limits the lifespan or lifetime of data in a computer or network *[NAT]: Network address translation -*[DnyDNS]: Dynamic DNS record pointing to a frequently changing IP address +*[UTF-8]: 8-bit Unicode Transformation Format - a character encoding format capable of encoding all known 1,112,064 valid Unicode characters +*[URL]: Uniform Ressource Locator, commonly known as "web address" +*[REST]: Representational State Transfer - a software architecture for distributed systems like the World Wide Web (WWW) diff --git a/docs/api/dns/cacheinfo.md b/docs/api/dns/cacheinfo.md new file mode 100644 index 0000000..888bfd8 --- /dev/null +++ b/docs/api/dns/cacheinfo.md @@ -0,0 +1,56 @@ +# DNS - Cache Info + +## GET: Obtain cache information + +Resource: `GET /admin/api/dns/cacheinfo` + +Requires authorization: No + +### Parameters + +None + +### Example + + +!!! example "Request" + + === "cURL" + + ``` bash + curl -H "Authorization: Token " \ + http://pi.hole:8080/admin/api/dns/cacheinfo + ``` + + === "Python 3" + + ``` python + import requests + + URL = 'http://pi.hole:8080/admin/api/dns/cacheinfo' + TOKEN = '' + HEADERS = {'Authorization': f'Token {TOKEN}'} + + response = requests.get(URL, headers=HEADERS) + + print(response.json()) + ``` + +!!! success "Response" + + Response code: `HTTP/1.1 200 OK` + + ``` json + { + "cache_size": 10000, + "cache_inserted": 509, + "cache_evicted": 0 + } + ``` + + +See [DNS cache details](../../ftldns/dns-cache.md) for further information about the returned quantities. + +This endpoint cannot fail. + +{!abbreviations.md!} diff --git a/docs/api/dns/lists.md b/docs/api/dns/lists.md new file mode 100644 index 0000000..7e34544 --- /dev/null +++ b/docs/api/dns/lists.md @@ -0,0 +1,273 @@ +# DNS - Domain Lists + +## GET: List all items + +Resources: + +- `GET /admin/api/dns/whitelist/exact` +- `GET /admin/api/dns/whitelist/regex` +- `GET /admin/api/dns/blacklist/exact` +- `GET /admin/api/dns/blacklist/regex` + +Requires authorization: Yes + +### Parameters + +None + +### Example + + +!!! example "Request" + + === "cURL" + + ``` bash + curl -H "Authorization: Token " \ + http://pi.hole:8080/admin/api/dns/whitelist/exact + ``` + + === "Python 3" + + ``` python + import requests + + URL = 'http://pi.hole:8080/admin/api/dns/whitelist/exact' + TOKEN = '' + HEADERS = {'Authorization': f'Token {TOKEN}'} + + response = requests.get(URL, headers=HEADERS) + + print(response.json()) + ``` + +!!! success "Success response" + + Response code: `HTTP/1.1 200 OK` + + ``` json + [ + { + "domain": "whitelisted.com", + "enabled": true, + "date_added": 1589108911, + "date_modified": 1589108911, + "comment": "" + } + ] + ``` + +!!! danger "Error response (database not available)" + + Response code: `HTTP/1.1 402 - Request failed` + + ``` json + { + "error": { + "key": "database_error", + "message": "Could not remove domain to gravity database", + "data": { + "sql_msg": "Database not available" + } + } + } + ``` + + +## PUT: Add item + +Resources: + +- `PUT /admin/api/dns/whitelist/exact` +- `PUT /admin/api/dns/whitelist/regex` +- `PUT /admin/api/dns/blacklist/exact` +- `PUT /admin/api/dns/blacklist/regex` + +Requires authorization: Yes + +### Parameters + +Name | Required | Type | Description | Default | Example +---- | -------- | ---- | ----------- | ------- | ------- +`domain` | Yes | String | Domain to be added | |`whitelisted.com` +`enabled` | Optional | Boolean | Should this domain be used? | `true` | `true` +`comment` | Optional | String | Comment for this domain | `null` | `Some text` + + +### Example + + +!!! example "Request" + + === "cURL" + + ``` bash + curl -X PUT \ + -H "Authorization: Token " \ + http://pi.hole:8080/admin/api/dns/whitelist/exact \ + -H "Content-Type: application/json" \ + -d @body.json + ``` + + === "Python 3" + + ``` python + import requests + + URL = 'http://pi.hole:8080/admin/api/dns/whitelist/exact' + TOKEN = '' + HEADERS = {'Authorization': f'Token {TOKEN}'} + data = json.load(open('body.json', 'rb')) + + response = requests.put( + URL, + json=data, + headers=HEADERS, + ) + + print(response.json()) + ``` + + The content of `body.json` is + + ``` json + { + "domain": "whitelisted.com", + "enabled": true, + "comment": "Some text" + } + ``` + +!!! success "Success response" + + Response code: `HTTP/1.1 201 Created` + + ``` json + { + "key": "added", + "domain": "whitelisted.com" + } + ``` + +!!! danger "Error response (duplicated domain)" + + Response code: `HTTP/1.1 402 - Request failed` + + ``` json + { + "error": { + "key": "database_error", + "message": "Could not add domain to gravity database", + "data": { + "domain": "whitelisted.com", + "enabled": true, + "comment": "Some text", + "sql_msg": "UNIQUE constraint failed: domainlist.domain" + } + } + } + ``` + + +--- + +## DELETE: Remove item + +Resources: + +- `DELETE /admin/api/dns/whitelist/exact/` +- `DELETE /admin/api/dns/whitelist/regex/` +- `DELETE /admin/api/dns/blacklist/exact/` +- `DELETE /admin/api/dns/blacklist/regex/` + +Requires authorization: Yes + +### Parameters + +The domain/regex to be removed is specified through the URL (``). + +### Example request + + +!!! example "Request" + === "cURL" + + **Domain** + + ``` bash + curl -X DELETE \ + -H "Authorization: Token " \ + http://pi.hole:8080/admin/api/dns/whitelist/exact/whitelisted.com + ``` + + **Regular expression** + + ``` bash + regex="$(echo "(^|\\.)facebook.com$" | jq -sRr '@uri')" + curl -X DELETE \ + -H "Authorization: Token " \ + http://pi.hole:8080/admin/api/dns/whitelist/exact/${regex} + ``` + + === "Python" + + **Domain** + + ``` python + import requests + + URL = 'http://pi.hole:8080/admin/api/dns/whitelist/exact/whitelisted.com' + TOKEN = '' + HEADERS = {'Authorization': f'Token {TOKEN}'} + + response = requests.delete(URL, headers=HEADERS) + + print(response.json()) + ``` + + **Regular expression** + + ``` python + import requests + import urllib + + regex = urllib.parse.quote("(^|\\.)facebook.com$") + URL = 'http://pi.hole:8080/admin/api/dns/whitelist/exact/' + TOKEN = '' + HEADERS = {'Authorization': f'Token {TOKEN}'} + + response = requests.delete(URL + regex, headers=HEADERS) + + print(response.json()) + ``` + +!!! success "Success response" + + Response code: `HTTP/1.1 200 OK` + + ``` json + { + "key": "removed", + "domain": "whitelisted.com" + } + ``` + +!!! danger "Error response (database permission error)" + + Response code: `HTTP/1.1 402 - Request failed` + + ```json + { + "error": { + "key": "database_error", + "message": "attempt to write a readonly databas", + "data": { + "domain": "whitelisted.com", + "sql_msg": "Database not available" + } + } + } + ``` + + +{!abbreviations.md!} diff --git a/docs/api/dns/status.md b/docs/api/dns/status.md new file mode 100644 index 0000000..15de7f2 --- /dev/null +++ b/docs/api/dns/status.md @@ -0,0 +1,123 @@ +# DNS - Status + +## GET: Obtain current blocking status + +Resource: `GET /admin/api/dns/status` + +Requires authorization: No + +### Parameters + +None + +### Examples + + +!!! example "Request" + + === "cURL" + + ``` bash + curl http://pi.hole:8080/admin/api/dns/status + ``` + + === "Python 3" + + ``` python + import requests + + URL = 'http://pi.hole:8080/admin/api/dns/status' + + response = requests.get(URL) + + print(response.json()) + ``` + +!!! success "Response" + + Response code: `HTTP/1.1 200 OK` + + ``` json + { + "status": "enabled" + } + ``` + + +## `POST`: Set/change blocking status + +Resource: `POST /admin/api/dns/status` + +Requires authorization: Yes + +### Parameters + +Name | Required | Type | Description | Default | Example +---- | -------- | ---- | ----------- | ------- | ------- +`action` | Yes | String | Requested status | | `enable` or `disable` +`time` | Optional | Number | Requested delay until opposite status is enabled | `0` | `100` (seconds) + +### Example + + +!!! example "Request" + + === "cURL" + + ``` bash + curl -X POST \ + -H "Authorization: Token " \ + http://pi.hole:8080/admin/api/dns/status \ + -H "Content-Type: application/json" \ + -d @body.json + ``` + + The content of `body.json` is like, + + ``` json + { + "action": "enable", + "time": 30 + } + ``` + + === "Python 3" + + ``` python + import requests + + URL = 'http://pi.hole:8080/admin/api/dns/status' + TOKEN = '' + HEADERS = {'Authorization': f'Token {TOKEN}'} + data = json.load(open('body.json', 'rb')) + + response = requests.post( + URL, + json=data, + headers=HEADERS, + ) + + print(response.json()) + ``` + + The content of `body.json` is like, + + ``` json + { + "action": "enable", + "time": 30 + } + ``` + +!!! success "Response" + + Response code: `HTTP/1.1 200 OK` + + ``` json + { + "key": "enabled" + } + ``` + + +{!abbreviations.md!} diff --git a/docs/api/index.md b/docs/api/index.md new file mode 100644 index 0000000..006e72e --- /dev/null +++ b/docs/api/index.md @@ -0,0 +1,126 @@ +# API Reference + +The Pi-hole API is organized around [REST](http://en.wikipedia.org/wiki/Representational_State_Transfer). Our API has predictable resource-oriented URLs, accepts [form-encoded](https://en.wikipedia.org/wiki/POST_(HTTP)#Use_for_submitting_web_forms) request bodies, returns reliable UTF-8 [JSON-encoded](http://www.json.org/) data for all API responses, and uses standard HTTP response codes, authentication, and verbs. + +## Authentication + +The Pi-hole API uses API keys to authenticate requests. You can view your API key in the Pi-hole Dashboard (**TODO: Link**). + +!!! warning + Your API key carries many privileges, so be sure to keep it secure! + Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth if your Pi-hole is reachable from the outside. + +The Authorization HTTP header can be specified with `Token ` to authenticate as a user and have the same permissions that the user itself. + + +!!! example active "Example request" + + === "cURL" + + ``` bash + curl -H "Authorization: Token " \ + http://pi.hole/admin/api/dns/status + ``` + + === "Python 3" + + ``` python + import requests + + URL = 'http://pi.hole/admin/api/dns/status' + TOKEN = '' + HEADERS = {'Authorization': f'Token {TOKEN}'} + + response = requests.get(URL, headers=HEADERS) + + print(response.json()) + ``` + +!!! success "Example reply: Success" + + Response code: `HTTP/1.1 200 OK` + + ``` json + { + "status": "enabled" + } + ``` + +!!! danger "Example reply: Error (unauthorized access)" + + Response code: `HTTP/1.1 401 Unauthorized` + + ``` json + { + "error": { + "key": "unauthorized", + "message": "Unauthorized", + "data": null + } + } + ``` + + +Most but not all endpoints require authentication. API requests requiring authentication will also fail if no key is supplied. + +## Errors + +Pi-hole uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the `2xx` range indicate success. Codes in the `4xx` range indicate an error that failed given the information provided (e.g., a required parameter was omitted, missing authentication, etc.). Codes in the `5xx` range indicate an error with Pi-hole's API (these are rare). + +Some `4xx` errors that could be handled programmatically include an error code that briefly explains the error reported. + +### HTTP code summary + +Code | Description | Interpretation +---- | ----------- | -------------- +`200` | `OK` | Everything worked as expected. +`400` | `Bad Request` | The request was unacceptable, often due to a missing required parameter. +`401` | `Unauthorized` | No valid API key provided for endpoint requiring authorization. +`402` | `Request Failed` | The parameters were valid but the request failed. +`403` | `Forbidden` | The API key doesn't have permissions to perform the request. +`404` | `Not Found` | The requested resource doesn't exist. +`429` | `Too Many Requests` | Too many requests hit the API too quickly. +`500`, `502`, `503`, `504` | `Server Errors` | Something went wrong on Pi-hole's end. (These are rare.) + +### JSON response + +The form of replies to successful requests strongly depends on the selected endpoint, e.g., + + +!!! success "Example reply: Success" + + Response code: `HTTP/1.1 200 OK` + + ``` json + { + "status": "enabled" + } + ``` + +In contrast, errors have a uniform appearance to ease a programatic treatment: + +!!! danger "Example reply: Error (unauthorized access)" + ``` json + { + "error": { + "key": "unauthorized", + "message": "Unauthorized", + "data": null + } + } + ``` + + +The items of the `error` object are always as follows: + +Field | Type | Description +----- | ---- | ----------- +`key` | String | Standardized key describing the error +`message` | String | Description of the error, may be shown to the user + + +In addition, `data` may contain a JSON object. This depends on the error itself and may contain further details such as the interpreted user data. If no additional data is available for this endpoint, `null` is returned instead of an object. + +We recommend writing code that gracefully handles all possible API exceptions. + +{!abbreviations.md!} diff --git a/docs/api/tbd.md b/docs/api/tbd.md new file mode 100644 index 0000000..172a255 --- /dev/null +++ b/docs/api/tbd.md @@ -0,0 +1,5 @@ +# API Documentation + +This topic is still to be written... + +{!abbreviations.md!} diff --git a/docs/ftldns/telnet-api.md b/docs/ftldns/telnet-api.md deleted file mode 100644 index 53cf00c..0000000 --- a/docs/ftldns/telnet-api.md +++ /dev/null @@ -1,281 +0,0 @@ -Connect via e.g. `telnet 127.0.0.1 4711` or use `echo ">command" | nc 127.0.0.1 4711` - -#### `>quit` {data-toc-label='quit'} - -Closes the connection to the client - ---- - -#### `>stats` {data-toc-label='stats'} - -Get current statistics - -```text -domains_being_blocked 116007 -dns_queries_today 30163 -ads_blocked_today 5650 -ads_percentage_today 18.731558 -unique_domains 1056 -queries_forwarded 4275 -queries_cached 20238 -clients_ever_seen 11 -unique_clients 9 -status enabled -``` - ---- - -#### `>overTime` {data-toc-label='overTime'} - -Get over time data (10 min intervals) - -```text -1525546500 163 0 -1525547100 154 1 -1525547700 164 0 -1525548300 167 0 -1525548900 151 0 -1525549500 143 0 -[...] -``` - ---- - -#### `>top-domains` {data-toc-label='top-domains'} - -Get top domains - -```text -0 8462 x.y.z.de -1 236 safebrowsing-cache.google.com -2 116 pi.hole -3 109 z.y.x.de -4 93 safebrowsing.google.com -5 96 plus.google.com -[...] -``` - -Variant: `>top-domains (15)` to show (up to) 15 entries - ---- - -#### `>top-ads` {data-toc-label='top-ads'} - -Get top ad domains - -```text -0 8 googleads.g.doubleclick.net -1 6 www.googleadservices.com -2 1 cdn.mxpnl.com -3 1 collector.githubapp.com -4 1 www.googletagmanager.com -5 1 s.zkcdn.net -[...] -``` - -Variant: `>top-ads (14)` to show (up to) 14 entries - ---- - -#### `>top-clients` {data-toc-label='top-clients'} - -Get recently active top clients (IP addresses + hostnames (if available)) - -```text -0 9373 192.168.2.1 router -1 484 192.168.2.2 work-machine -2 8 127.0.0.1 localhost -``` - -Variant: `>top-clients (9)` to show (up to) 9 client entries or `>top-clients withzero (15)` to show (up to) 15 clients even if they have not been active recently (see PR #124 for further details) - ---- - -#### `>forward-dest` {data-toc-label='forward-dest'} - -Get forward destinations (IP addresses + hostnames (if available)) along with the percentage. The first result (ID -2) will always be the percentage of domains answered from blocklists, whereas the second result (ID -1) will be the queries answered from the cache - -```text --2 18.70 blocklist blocklist --1 67.10 cache cache -0 14.20 127.0.0.1 localhost -``` - -Variant: `>forward-dest unsorted` to show forward destinations in unsorted order (equivalent to using `>forward-names`) - ---- - -#### `>querytypes` {data-toc-label='querytypes'} - -Get collected query types percentage - -```text -A (IPv4): 53.45 -AAAA (IPv6): 45.32 -ANY: 0.00 -SRV: 0.64 -SOA: 0.05 -PTR: 0.54 -TXT: 0.00 -``` - ---- - -#### `>getallqueries` {data-toc-label='getallqueries'} - -Get all queries that FTL has in memory - -```text -1525554586 A fonts.googleapis.com 192.168.2.100 3 0 4 6 -1525554586 AAAA fonts.googleapis.com 192.168.2.100 3 0 4 5 -1525554586 A www.mkdocs.org 192.168.2.100 3 0 4 7 -1525554586 AAAA www.mkdocs.org 192.168.2.100 2 0 3 21 -1525554586 A squidfunk.github.io 192.168.2.100 2 0 3 20 -1525554586 A pi-hole.net 192.168.2.100 3 0 4 5 -1525554586 AAAA squidfunk.github.io 192.168.2.100 3 0 1 6 -1525554586 AAAA pi-hole.net 192.168.2.100 2 0 1 18 -1525554586 A github.com 192.168.2.100 3 0 4 5 -1525554586 AAAA github.com 192.168.2.100 2 0 1 18 -``` - -Variants: - -- `>getallqueries (37)` show (up to) 37 latest entries, -- `>getallqueries-time 1483964295 1483964312` gets all queries that FTL has in its database in a limited time interval, -- `>getallqueries-time 1483964295 1483964312 (17)` show matches in the (up to) 17 latest entries, -- `>getallqueries-domain www.google.com` gets all queries that FTL has in its database for a specific domain name, -- `>getallqueries-client 2.3.4.5`: gets all queries that FTL has in its database for a specific client name *or* IP - ---- - -#### `>recentBlocked` {data-toc-label='recentBlocked'} - -Get most recently pi-holed domain name - -```text -www.googleadservices.com -``` - -Variant: `>recentBlocked (4)` show the four most recent blocked domains - ---- - -#### `>clientID` {data-toc-label='clientID'} - -Get ID of currently connected client - -```text -6 -``` - ---- - -#### `>version` {data-toc-label='version'} - -Get version information of the currently running FTL instance - -```text -version v1.6-3-g106498d-dirty -tag v1.6 -branch master -hash 106498d -date 2017-03-26 13:10:43 +0200 -``` - ---- - -#### `>dbstats` {data-toc-label='dbstats'} - -Get some statistics about `FTL`'s' long-term storage database (this request may take some time for processing in case of a large database file) - -```text -queries in database: 2700304 -database filesize: 199.20 MB -SQLite version: 3.23.1 -``` - ---- - -#### `>domain pi-hole.net` {data-toc-label='domain'} - -Get detailed information about domain (if available) - -```text -Domain "pi-hole.net", ID: 254 -Total: 179 -Blocked: 0 -Wildcard blocked: false -``` - ---- - -#### `>cacheinfo` {data-toc-label='cacheinfo'} - -Get DNS server cache size and usage information - -```text -cache-size: 500000 -cache-live-freed: 0 -cache-inserted: 15529 -``` - ---- - -#### `>dns-port` {data-toc-label='dns-port'} - -Get DNS port FTL is listening on - -```text -53 -``` - -Note that the port can also be `0` if someone decides to disable the DNS server part of Pi-hole - ---- - -#### `>maxlogage` {data-toc-label='maxlogage'} - -Get timespan of the statistics shown on the dashboard (in seconds) - -```text -86400 -``` - ---- - -#### `>gateway` {data-toc-label='gateway'} - -Get the IP of the gateway of the default route and the corresponding interface - -```text -192.168.0.1 enp2s0 -``` - -Note that if no non-default route could be found, `0.0.0.0` and an empty interface string is returned - ---- - -#### `>interfaces` {data-toc-label='interfaces'} - -Get extended information of the interfaces of th Pi-hole device - -```text -eth0 UP 1000 2.2GB 5.6GB 10.0.1.5 fd00:e57b:XXXX:210e:1a1,2a01:XXXX:c15b,fe80::2e5c:XXXX:4060 -wlan0 DOWN -1 0.0B 0.0B - - -docker0 UP 10000 837.6MB 300.7MB 172.17.0.1,169.254.241.237 - -lo UP -1 48.0MB 48.0MB 127.0.0.1 - -wg0 UP -1 1.0GB 141.2MB 10.0.40.1 - -sum UP 0 4.2GB 6.1GB - - -``` - -Column definitions are: - -1. Interface name -2. UP/DOWN status -3. Link speed in MBit/s (-1 means "Not available" (like link down) or "Not applicable" (like virtual interface)) -4. TX bytes -5. RX bytes -6. Associated IPv4 addresses -7. Associated IPv6 addresses - -The default interface (the one connected to the gateway) will always be the first. The sum will always be the last one - even if you have (for whatever reason) an interface called sum. Regarding the link speed: It won't work for most WiFi interfaces as the speed is not known at the kernel level. Instead, the drivers manage them dynamically depending on package loss, signal strength, etc. - in this case, you'll see link speed -1 as well diff --git a/mkdocs.yml b/mkdocs.yml index f2cdc81..642f077 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -85,6 +85,15 @@ markdown_extensions: # https://squidfunk.github.io/mkdocs-material/reference/tooltips/#adding-a-glossary auto_append: - docs/abbreviations.md + # Include files in other documents like {!some/dir/in/docs/filename.md!} + - markdown_include.include: + base_path: docs + # Metadata support in pages + # (https://squidfunk.github.io/mkdocs-material/extensions/metadata/) + - meta + # Tabbed provides a syntax to easily add tabbed Markdown content. + # (https://facelessuser.github.io/pymdown-extensions/extensions/tabbed/) + - pymdownx.tabbed nav: - Overview: index.md @@ -107,6 +116,25 @@ nav: - 'Overview': database/gravity/index.md - 'Group management': database/gravity/groups.md - 'Database recovery': database/gravity/recovery.md + - 'Examples': database/gravity/example.md + - 'Pi-hole API': + - 'Overview': api/index.md + - 'DNS': + - 'Status': api/dns/status.md + - 'Domain Lists': api/dns/lists.md + - 'Cache Info': api/dns/cacheinfo.md + - 'FTL': + - 'Network': api/tbd.md + - 'Logs': api/tbd.md + - 'Statistics': + - 'Summary': api/tbd.md + - 'Over time': api/tbd.md + - 'Upstreams': api/tbd.md + - 'Top Items': api/tbd.md + - 'History': api/tbd.md + - 'Version': api/tbd.md + - 'Authentication': api/tbd.md + - 'Settings': api/tbd.md - 'FTLDNS': - 'Overview': ftldns/index.md - 'Configuration': ftldns/configfile.md @@ -135,6 +163,10 @@ nav: - "Tutorial": regex/tutorial.md - "Pi-hole extensions": regex/pi-hole.md - "Approximate matching": regex/approximate.md + - 'Compatibility': ftldns/compatibility.md + - 'Install from source': ftldns/compile.md + - 'Debugging FTLDNS': ftldns/debugging.md + - 'In-depth manual': ftldns/in-depth.md - 'Docker': - 'DHCP': docker/DHCP.md - 'Contributing':