diff --git a/docs/build.py b/docs/build.py
index 479841e80..67ce6c3fe 100644
--- a/docs/build.py
+++ b/docs/build.py
@@ -10,7 +10,7 @@ for script in (here / "scripts").glob("*.py"):
print(f"Generating output for {script.name}...")
out = subprocess.check_output(["python3", script.absolute()], text=True)
if out:
- (here / "src" / "generated" / f"{script.stem}.html").write_text(out)
+ (here / "src" / "generated" / f"{script.stem}.html").write_text(out, encoding="utf8")
if (here / "public").exists():
shutil.rmtree(here / "public")
diff --git a/docs/scripts/api-events.py b/docs/scripts/api-events.py
index 31b0f3301..6a8b331f0 100644
--- a/docs/scripts/api-events.py
+++ b/docs/scripts/api-events.py
@@ -13,7 +13,7 @@ from mitmproxy.proxy.layers import http, tcp, tls, websocket
known = set()
-def category(name: str, hooks: List[Type[hooks.Hook]]) -> None:
+def category(name: str, desc: str, hooks: List[Type[hooks.Hook]]) -> None:
all_params = [
list(inspect.signature(hook.__init__).parameters.values())[1:]
for hook in hooks
@@ -42,7 +42,8 @@ def category(name: str, hooks: List[Type[hooks.Hook]]) -> None:
print(f"import {imp}")
print()
- print(f"class {name}_Events:")
+ print(f"class {name}Events:")
+ print(f' """{desc}"""')
first = True
for hook, params in zip(hooks, all_params):
@@ -54,7 +55,7 @@ def category(name: str, hooks: List[Type[hooks.Hook]]) -> None:
raise RuntimeError(f"Already documented: {hook}")
known.add(hook.name)
doc = inspect.getdoc(hook)
- print(f" def {hook.name}({', '.join(str(p) for p in params)}):")
+ print(f" def {hook.name}({', '.join(str(p) for p in ['self'] + params)}):")
print(textwrap.indent(f'"""\n{doc}\n"""', " "))
if params:
print(f' ctx.log(f"{hook.name}: {" ".join("{" + p.name + "=}" for p in params)}")')
@@ -66,9 +67,11 @@ def category(name: str, hooks: List[Type[hooks.Hook]]) -> None:
outfile = Path(__file__).parent.parent / "src" / "generated" / "events.py"
with outfile.open("w") as f, contextlib.redirect_stdout(f):
+ print("# This file is autogenerated, do not edit manually.")
category(
"Lifecycle",
+ "",
[
addonmanager.LoadHook,
hooks.RunningHook,
@@ -79,6 +82,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"Connection",
+ "",
[
server_hooks.ClientConnectedHook,
server_hooks.ClientDisconnectedHook,
@@ -90,6 +94,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"HTTP",
+ "",
[
http.HttpRequestHeadersHook,
http.HttpRequestHook,
@@ -102,6 +107,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"TCP",
+ "",
[
tcp.TcpStartHook,
tcp.TcpMessageHook,
@@ -112,6 +118,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"TLS",
+ "",
[
tls.TlsClienthelloHook,
tls.TlsStartHook,
@@ -120,6 +127,7 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
category(
"WebSocket",
+ "",
[
websocket.WebsocketStartHook,
websocket.WebsocketMessageHook,
@@ -129,7 +137,8 @@ with outfile.open("w") as f, contextlib.redirect_stdout(f):
)
category(
- "Advanced_Lifecycle",
+ "AdvancedLifecycle",
+ "",
[
layer.NextLayerHook,
hooks.UpdateHook,
diff --git a/docs/scripts/api-render.py b/docs/scripts/api-render.py
index 11f5714cc..4e3a9cd10 100644
--- a/docs/scripts/api-render.py
+++ b/docs/scripts/api-render.py
@@ -3,7 +3,7 @@ import os
import shutil
from pathlib import Path
-import pdoc.render
+import pdoc.render_helpers
here = Path(__file__).parent
@@ -18,14 +18,19 @@ pdoc.render.configure(
template_directory=here / "pdoc-template",
edit_url_map=edit_url_map,
)
+# We can't configure Hugo, but we can configure pdoc.
+pdoc.render_helpers.formatter.cssclass = "chroma"
modules = [
- here / ".." / "src" / "generated" / "events.py",
- "mitmproxy.proxy.context",
- "mitmproxy.http",
+ "mitmproxy.addonmanager",
+ "mitmproxy.certs",
+ #"mitmproxy.connection",
"mitmproxy.flow",
+ "mitmproxy.http",
+ "mitmproxy.proxy.server_hooks",
"mitmproxy.tcp",
"mitmproxy.websocket",
+ here / ".." / "src" / "generated" / "events.py",
]
pdoc.pdoc(
@@ -42,7 +47,7 @@ api_content.mkdir()
for module in modules:
if isinstance(module, Path):
continue
- filename = f"api/{ module.replace('.','/') }.html"
+ filename = f"api/{module.replace('.', '/')}.html"
(api_content / f"{module}.md").write_text(f"""
---
title: "{module}"
@@ -55,3 +60,5 @@ menu:
{{{{< readfile file="/generated/{filename}" >}}}}
""")
+
+(here / ".." / "src" / "content" / "addons-api.md").touch()
diff --git a/docs/scripts/examples.py b/docs/scripts/examples.py
index c0209274e..d5abd2ed3 100755
--- a/docs/scripts/examples.py
+++ b/docs/scripts/examples.py
@@ -31,7 +31,7 @@ for example in examples:
f" * [{example.name}](#{slug}){comment}"
)
listings.append(f"""
-
Example: {example.name}
+Example: {example.name}
```python
{code}
diff --git a/docs/scripts/pdoc-template/module.html.jinja2 b/docs/scripts/pdoc-template/module.html.jinja2
index 4aebdc4f4..48db197b0 100644
--- a/docs/scripts/pdoc-template/module.html.jinja2
+++ b/docs/scripts/pdoc-template/module.html.jinja2
@@ -1,25 +1,20 @@
{% extends "default/module.html.jinja2" %}
{% block nav %}{% endblock %}
{% block style_layout %}{% endblock %}
-
+{% block style_pygments %}{% endblock %}
{#
-We do a bit of hackery here: generated/events.py is automatically created by scripts/api-events.py,
-and then documented using a heavily customized style.
+To document all event hooks, we do a bit of hackery:
+ 1. scripts/api-events.py auto-generates generated/events.py.
+ 2. scripts/api-render.py renders generated/events.py together with the remaining API docs.
+ 3. This templates hides some elements of the default pdoc template.
+
#}
{% if module.name == "events" %}
{% macro module_name() %}
-
{% endmacro %}
{% macro view_source(doc) %}
- {% if doc.type == "function" %}
-
- View Source
- {{ doc.source | dedent | highlight }}
-
+ {% if doc.type != "module" %}
+ {{ default_view_source(doc) }}
{% endif %}
{% endmacro %}
{% macro is_public(doc) %}
@@ -27,10 +22,18 @@ and then documented using a heavily customized style.
{{ default_is_public(doc) }}
{% endif %}
{% endmacro %}
- {% macro class(cls) %}
-
- {{ headerlink(cls) }}
- {{ cls.name.replace("_", " ") }}
-
+{% else %}
+ {% macro is_public(doc) %}
+ {% if doc.modulename == "mitmproxy.addonmanager" %}
+ {% if doc.qualname.startswith("Loader") and not doc.name.startswith("_") %}
+ true
+ {% endif %}
+ {% elif doc.modulename == "mitmproxy.certs" %}
+ {% if doc.qualname == "Cert" or doc.qualname.startswith("Cert.") %}
+ {{ default_is_public(doc) }}
+ {% endif %}
+ {% elif doc.name is not in(["from_state", "get_state", "set_state"]) %}
+ {{ default_is_public(doc) }}
+ {% endif %}
{% endmacro %}
{% endif %}
diff --git a/docs/src/assets/style.scss b/docs/src/assets/style.scss
index 818680358..3f39cea92 100644
--- a/docs/src/assets/style.scss
+++ b/docs/src/assets/style.scss
@@ -1,4 +1,12 @@
@import "./syntax";
+/* background for both hugo *and* pdoc. */
+.chroma pre, pre.chroma {
+ background-color: #f7f7f7;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ padding: .5rem 0 .5rem .5rem;
+}
+
@import "./badge";
$primary: #C93312;
@@ -20,6 +28,10 @@ bulma.io v0.8.0 | MIT License | github.com/jgthms/bulma */
@import "./bulma/components/_all";
@import "./bulma/layout/_all";
+html {
+ scroll-behavior: smooth;
+}
+
html, body {
height: 100%;
}
diff --git a/docs/src/assets/syntax.css b/docs/src/assets/syntax.css
index 33ce367c9..23be04464 100644
--- a/docs/src/assets/syntax.css
+++ b/docs/src/assets/syntax.css
@@ -1,59 +1,82 @@
-/* Background */ .chroma { color: #f8f8f2; background-color: #272822 }
-/* Error */ .chroma .err { color: #960050; background-color: #1e0010 }
+/* Background */ .chroma { background-color: #ffffff }
+/* Other */ .chroma .x { }
+/* Error */ .chroma .err { color: #a61717; background-color: #e3d2d2 }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
-/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: 100%; overflow: auto; display: block; }
+/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
-/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; display: block; }
-/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; }
-/* Keyword */ .chroma .k { color: #66d9ef }
-/* KeywordConstant */ .chroma .kc { color: #66d9ef }
-/* KeywordDeclaration */ .chroma .kd { color: #66d9ef }
-/* KeywordNamespace */ .chroma .kn { color: #f92672 }
-/* KeywordPseudo */ .chroma .kp { color: #66d9ef }
-/* KeywordReserved */ .chroma .kr { color: #66d9ef }
-/* KeywordType */ .chroma .kt { color: #66d9ef }
-/* NameAttribute */ .chroma .na { color: #a6e22e }
-/* NameClass */ .chroma .nc { color: #a6e22e }
-/* NameConstant */ .chroma .no { color: #66d9ef }
-/* NameDecorator */ .chroma .nd { color: #a6e22e }
-/* NameException */ .chroma .ne { color: #a6e22e }
-/* NameFunction */ .chroma .nf { color: #a6e22e }
-/* NameOther */ .chroma .nx { color: #a6e22e }
-/* NameTag */ .chroma .nt { color: #f92672 }
-/* Literal */ .chroma .l { color: #ae81ff }
-/* LiteralDate */ .chroma .ld { color: #e6db74 }
-/* LiteralString */ .chroma .s { color: #e6db74 }
-/* LiteralStringAffix */ .chroma .sa { color: #e6db74 }
-/* LiteralStringBacktick */ .chroma .sb { color: #e6db74 }
-/* LiteralStringChar */ .chroma .sc { color: #e6db74 }
-/* LiteralStringDelimiter */ .chroma .dl { color: #e6db74 }
-/* LiteralStringDoc */ .chroma .sd { color: #e6db74 }
-/* LiteralStringDouble */ .chroma .s2 { color: #e6db74 }
-/* LiteralStringEscape */ .chroma .se { color: #ae81ff }
-/* LiteralStringHeredoc */ .chroma .sh { color: #e6db74 }
-/* LiteralStringInterpol */ .chroma .si { color: #e6db74 }
-/* LiteralStringOther */ .chroma .sx { color: #e6db74 }
-/* LiteralStringRegex */ .chroma .sr { color: #e6db74 }
-/* LiteralStringSingle */ .chroma .s1 { color: #e6db74 }
-/* LiteralStringSymbol */ .chroma .ss { color: #e6db74 }
-/* LiteralNumber */ .chroma .m { color: #ae81ff }
-/* LiteralNumberBin */ .chroma .mb { color: #ae81ff }
-/* LiteralNumberFloat */ .chroma .mf { color: #ae81ff }
-/* LiteralNumberHex */ .chroma .mh { color: #ae81ff }
-/* LiteralNumberInteger */ .chroma .mi { color: #ae81ff }
-/* LiteralNumberIntegerLong */ .chroma .il { color: #ae81ff }
-/* LiteralNumberOct */ .chroma .mo { color: #ae81ff }
-/* Operator */ .chroma .o { color: #f92672 }
-/* OperatorWord */ .chroma .ow { color: #f92672 }
-/* Comment */ .chroma .c { color: #75715e }
-/* CommentHashbang */ .chroma .ch { color: #75715e }
-/* CommentMultiline */ .chroma .cm { color: #75715e }
-/* CommentSingle */ .chroma .c1 { color: #75715e }
-/* CommentSpecial */ .chroma .cs { color: #75715e }
-/* CommentPreproc */ .chroma .cp { color: #75715e }
-/* CommentPreprocFile */ .chroma .cpf { color: #75715e }
-/* GenericDeleted */ .chroma .gd { color: #f92672 }
-/* GenericEmph */ .chroma .ge { font-style: italic }
-/* GenericInserted */ .chroma .gi { color: #a6e22e }
+/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* Keyword */ .chroma .k { color: #000000; font-weight: bold }
+/* KeywordConstant */ .chroma .kc { color: #000000; font-weight: bold }
+/* KeywordDeclaration */ .chroma .kd { color: #000000; font-weight: bold }
+/* KeywordNamespace */ .chroma .kn { color: #000000; font-weight: bold }
+/* KeywordPseudo */ .chroma .kp { color: #000000; font-weight: bold }
+/* KeywordReserved */ .chroma .kr { color: #000000; font-weight: bold }
+/* KeywordType */ .chroma .kt { color: #445588; font-weight: bold }
+/* Name */ .chroma .n { }
+/* NameAttribute */ .chroma .na { color: #008080 }
+/* NameBuiltin */ .chroma .nb { color: #0086b3 }
+/* NameBuiltinPseudo */ .chroma .bp { color: #999999 }
+/* NameClass */ .chroma .nc { color: #445588; font-weight: bold }
+/* NameConstant */ .chroma .no { color: #008080 }
+/* NameDecorator */ .chroma .nd { color: #3c5d5d; font-weight: bold }
+/* NameEntity */ .chroma .ni { color: #800080 }
+/* NameException */ .chroma .ne { color: #990000; font-weight: bold }
+/* NameFunction */ .chroma .nf { color: #990000; font-weight: bold }
+/* NameFunctionMagic */ .chroma .fm { }
+/* NameLabel */ .chroma .nl { color: #990000; font-weight: bold }
+/* NameNamespace */ .chroma .nn { color: #555555 }
+/* NameOther */ .chroma .nx { }
+/* NameProperty */ .chroma .py { }
+/* NameTag */ .chroma .nt { color: #000080 }
+/* NameVariable */ .chroma .nv { color: #008080 }
+/* NameVariableClass */ .chroma .vc { color: #008080 }
+/* NameVariableGlobal */ .chroma .vg { color: #008080 }
+/* NameVariableInstance */ .chroma .vi { color: #008080 }
+/* NameVariableMagic */ .chroma .vm { }
+/* Literal */ .chroma .l { }
+/* LiteralDate */ .chroma .ld { }
+/* LiteralString */ .chroma .s { color: #dd1144 }
+/* LiteralStringAffix */ .chroma .sa { color: #dd1144 }
+/* LiteralStringBacktick */ .chroma .sb { color: #dd1144 }
+/* LiteralStringChar */ .chroma .sc { color: #dd1144 }
+/* LiteralStringDelimiter */ .chroma .dl { color: #dd1144 }
+/* LiteralStringDoc */ .chroma .sd { color: #dd1144 }
+/* LiteralStringDouble */ .chroma .s2 { color: #dd1144 }
+/* LiteralStringEscape */ .chroma .se { color: #dd1144 }
+/* LiteralStringHeredoc */ .chroma .sh { color: #dd1144 }
+/* LiteralStringInterpol */ .chroma .si { color: #dd1144 }
+/* LiteralStringOther */ .chroma .sx { color: #dd1144 }
+/* LiteralStringRegex */ .chroma .sr { color: #009926 }
+/* LiteralStringSingle */ .chroma .s1 { color: #dd1144 }
+/* LiteralStringSymbol */ .chroma .ss { color: #990073 }
+/* LiteralNumber */ .chroma .m { color: #009999 }
+/* LiteralNumberBin */ .chroma .mb { color: #009999 }
+/* LiteralNumberFloat */ .chroma .mf { color: #009999 }
+/* LiteralNumberHex */ .chroma .mh { color: #009999 }
+/* LiteralNumberInteger */ .chroma .mi { color: #009999 }
+/* LiteralNumberIntegerLong */ .chroma .il { color: #009999 }
+/* LiteralNumberOct */ .chroma .mo { color: #009999 }
+/* Operator */ .chroma .o { color: #000000; font-weight: bold }
+/* OperatorWord */ .chroma .ow { color: #000000; font-weight: bold }
+/* Punctuation */ .chroma .p { }
+/* Comment */ .chroma .c { color: #999988; font-style: italic }
+/* CommentHashbang */ .chroma .ch { color: #999988; font-style: italic }
+/* CommentMultiline */ .chroma .cm { color: #999988; font-style: italic }
+/* CommentSingle */ .chroma .c1 { color: #999988; font-style: italic }
+/* CommentSpecial */ .chroma .cs { color: #999999; font-weight: bold; font-style: italic }
+/* CommentPreproc */ .chroma .cp { color: #999999; font-weight: bold; font-style: italic }
+/* CommentPreprocFile */ .chroma .cpf { color: #999999; font-weight: bold; font-style: italic }
+/* Generic */ .chroma .g { }
+/* GenericDeleted */ .chroma .gd { color: #000000; background-color: #ffdddd }
+/* GenericEmph */ .chroma .ge { color: #000000; font-style: italic }
+/* GenericError */ .chroma .gr { color: #aa0000 }
+/* GenericHeading */ .chroma .gh { color: #999999 }
+/* GenericInserted */ .chroma .gi { color: #000000; background-color: #ddffdd }
+/* GenericOutput */ .chroma .go { color: #888888 }
+/* GenericPrompt */ .chroma .gp { color: #555555 }
/* GenericStrong */ .chroma .gs { font-weight: bold }
-/* GenericSubheading */ .chroma .gu { color: #75715e }
+/* GenericSubheading */ .chroma .gu { color: #aaaaaa }
+/* GenericTraceback */ .chroma .gt { color: #aa0000 }
+/* GenericUnderline */ .chroma .gl { text-decoration: underline }
+/* TextWhitespace */ .chroma .w { color: #bbbbbb }
diff --git a/docs/src/config.toml b/docs/src/config.toml
index fa6f3dc6c..0d26241bc 100644
--- a/docs/src/config.toml
+++ b/docs/src/config.toml
@@ -5,6 +5,7 @@ theme = "mitmproxydocs"
publishDir = "../public"
RelativeURLs = true
pygmentsCodefences = true
+pygmentsUseClasses = true
[indexes]
tag = "tags"
diff --git a/docs/src/content/addons-api.md b/docs/src/content/addons-api.md
index eaa18f45f..0903048e1 100644
--- a/docs/src/content/addons-api.md
+++ b/docs/src/content/addons-api.md
@@ -25,7 +25,8 @@ header with a count of the number of responses seen:
{{< example src="examples/addons/http-add-header.py" lang="py" >}}
-## Addon Events
+## Example Addons
+The following addons showcase all available event hooks.
{{< readfile file="/generated/api/events.html" >}}
diff --git a/docs/src/content/addons-examples.md b/docs/src/content/addons-examples.md
index eaa4c7557..941f189df 100644
--- a/docs/src/content/addons-examples.md
+++ b/docs/src/content/addons-examples.md
@@ -1,6 +1,6 @@
---
-title: "Example Addons"
+title: "Examples"
menu:
addons:
weight: 6
diff --git a/docs/src/content/addons-overview.md b/docs/src/content/addons-overview.md
index cc35eaa28..6aa44a46d 100644
--- a/docs/src/content/addons-overview.md
+++ b/docs/src/content/addons-overview.md
@@ -7,13 +7,11 @@ menu:
# Addons
-Mitmproxy's addon mechanism consists of a set of APIs that support components of
-any complexity. Addons interact with mitmproxy by responding to **events**,
-which allow them to hook into and change mitmproxy's behaviour. They are
-configured through **[options]({{< relref concepts-options >}})**, which can be
-set in mitmproxy's config file, changed interactively by users, or passed on the
-command-line. Finally, they can expose **commands**, which allows users to
-invoke their actions either directly or by binding them to keys in the
+Mitmproxy's addon mechanism consists of a set of APIs that support components of any complexity. Addons interact with
+mitmproxy by responding to [events]({{< relref addons-api >}}), which allow them to hook into and change mitmproxy's
+behaviour. They are configured through [options]({{< relref addons-options >}}), which can be set in mitmproxy's config
+file, changed interactively by users, or passed on the command-line. Finally, they can expose [commands]({{< relref
+addons-commands >}}), which allows users to invoke their actions either directly or by binding them to keys in the
interactive tools.
Addons are an exceptionally powerful part of mitmproxy. In fact, much of
@@ -30,16 +28,16 @@ functionality to third-party scripters and extenders.
This document will show you how to build addons using **events**, **options**
and **commands**. However, this is not an API manual, and the mitmproxy source
code remains the canonical reference. One easy way to explore the API from the
-command-line is to use [pydoc](https://docs.python.org/3/library/pydoc.html).
+command-line is to use [pdoc](https://pdoc.dev/).
Here, for example, is a command that shows the API documentation for the
mitmproxy's HTTP flow classes:
```bash
-pydoc mitmproxy.http
+pdoc mitmproxy.http
```
You will be referring to the mitmproxy API documentation frequently, so keep
-**pydoc** or an equivalent handy.
+**pdoc** or an equivalent handy.
# Anatomy of an addon
@@ -55,7 +53,7 @@ it into your mitmproxy tool of choice. We'll use mitmpdump in these examples,
but the flag is identical for all tools:
```bash
-> mitmdump -s ./anatomy.py
+mitmdump -s ./anatomy.py
```
Here are a few things to note about the code above:
@@ -63,10 +61,9 @@ Here are a few things to note about the code above:
- Mitmproxy picks up the contents of the `addons` global list and loads what it
finds into the addons mechanism.
- Addons are just objects - in this case our addon is an instance of `Counter`.
-- The `request` method is an example of an **event**. Addons simply implement a
- method for each event they want to handle. Each event has a signature
- consisting of arguments that are passed to the method. For `request`, this is
- an instance of `mitmproxy.http.HTTPFlow`.
+- The `request` method is an example of an *event*. Addons simply implement a
+ method for each event they want to handle. Each event and its signature are documented
+ in the [API documentation]({{< relref "addons-api" >}}).
- Finally, the `ctx` module is a holdall module that exposes a set of standard
objects that are commonly used in addons. We could pass a `ctx` object as the
first parameter to every event, but we've found it neater to just expose it as
diff --git a/docs/src/content/api/mitmproxy.addonmanager.md b/docs/src/content/api/mitmproxy.addonmanager.md
new file mode 100644
index 000000000..091afcb54
--- /dev/null
+++ b/docs/src/content/api/mitmproxy.addonmanager.md
@@ -0,0 +1,11 @@
+
+---
+title: "mitmproxy.addonmanager"
+url: "api/mitmproxy/addonmanager.html"
+
+menu:
+ addons:
+ parent: 'API'
+---
+
+{{< readfile file="/generated/api/mitmproxy/addonmanager.html" >}}
diff --git a/docs/src/content/api/mitmproxy.certs.md b/docs/src/content/api/mitmproxy.certs.md
new file mode 100644
index 000000000..4ab6aa561
--- /dev/null
+++ b/docs/src/content/api/mitmproxy.certs.md
@@ -0,0 +1,11 @@
+
+---
+title: "mitmproxy.certs"
+url: "api/mitmproxy/certs.html"
+
+menu:
+ addons:
+ parent: 'API'
+---
+
+{{< readfile file="/generated/api/mitmproxy/certs.html" >}}
diff --git a/docs/src/content/api/mitmproxy.proxy.context.md b/docs/src/content/api/mitmproxy.proxy.context.md
deleted file mode 100644
index a11abe674..000000000
--- a/docs/src/content/api/mitmproxy.proxy.context.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
----
-title: "mitmproxy.proxy.context"
-url: "api/mitmproxy/proxy/context.html"
-
-menu:
- addons:
- parent: 'API'
----
-
-{{< readfile file="/generated/api/mitmproxy/proxy/context.html" >}}
diff --git a/docs/src/content/api/mitmproxy.proxy.server_hooks.md b/docs/src/content/api/mitmproxy.proxy.server_hooks.md
new file mode 100644
index 000000000..a18806728
--- /dev/null
+++ b/docs/src/content/api/mitmproxy.proxy.server_hooks.md
@@ -0,0 +1,11 @@
+
+---
+title: "mitmproxy.proxy.server_hooks"
+url: "api/mitmproxy/proxy/server_hooks.html"
+
+menu:
+ addons:
+ parent: 'API'
+---
+
+{{< readfile file="/generated/api/mitmproxy/proxy/server_hooks.html" >}}