diff --git a/opendbc/car/__init__.py b/opendbc/car/__init__.py
index 455d8c3803..49e92c30ea 100644
--- a/opendbc/car/__init__.py
+++ b/opendbc/car/__init__.py
@@ -8,7 +8,7 @@
from panda import uds
from opendbc.car import structs
from opendbc.car.can_definitions import CanData
-from opendbc.car.docs_definitions import CarDocs
+from opendbc.car.docs.definitions import CarDocs
from opendbc.car.common.numpy_fast import clip, interp
# set up logging
diff --git a/opendbc/car/body/values.py b/opendbc/car/body/values.py
index 3c221548c3..dfe1e8d479 100644
--- a/opendbc/car/body/values.py
+++ b/opendbc/car/body/values.py
@@ -1,6 +1,6 @@
from opendbc.car import CarSpecs, PlatformConfig, Platforms, dbc_dict
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarDocs
+from opendbc.car.docs.definitions import CarDocs
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = CarParams.Ecu
diff --git a/opendbc/car/chrysler/values.py b/opendbc/car/chrysler/values.py
index 472b707f9d..cb6d130638 100644
--- a/opendbc/car/chrysler/values.py
+++ b/opendbc/car/chrysler/values.py
@@ -4,7 +4,7 @@
from panda import uds
from opendbc.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
+from opendbc.car.docs.definitions import CarHarness, CarDocs, CarParts
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, p16
Ecu = CarParams.Ecu
diff --git a/opendbc/car/docs/CARS_template.md b/opendbc/car/docs/CARS_template.md
new file mode 100644
index 0000000000..2f49e79b81
--- /dev/null
+++ b/opendbc/car/docs/CARS_template.md
@@ -0,0 +1,74 @@
+{% set footnote_tag = '[{}](#footnotes)' %}
+{% set star_icon = '[![star](assets/icon-star-{}.svg)](##)' %}
+{% set video_icon = '' %}
+{# Force hardware column wider by using a blank image with max width. #}
+{% set width_tag = '%s
' %}
+{% set hardware_col_name = 'Hardware Needed' %}
+{% set wide_hardware_col_name = width_tag|format(hardware_col_name) -%}
+
+
+
+# Supported Cars
+
+A supported vehicle is one that just works when you install a comma device. All supported cars provide a better experience than any stock system. Supported vehicles reference the US market unless otherwise specified.
+
+# {{all_car_docs | length}} Supported Cars
+
+|{{Column | map(attribute='value') | join('|') | replace(hardware_col_name, wide_hardware_col_name)}}|
+|---|---|---|{% for _ in range((Column | length) - 3) %}{{':---:|'}}{% endfor +%}
+{% for car_docs in all_car_docs %}
+|{% for column in Column %}{{car_docs.get_column(column, star_icon, video_icon, footnote_tag)}}|{% endfor %}
+
+{% endfor %}
+
+### Footnotes
+{% for footnote in footnotes %}
+{{loop.index}}{{footnote}}
+{% endfor %}
+
+## Community Maintained Cars
+Although they're not upstream, the community has openpilot running on other makes and models. See the 'Community Supported Models' section of each make [on our wiki](https://wiki.comma.ai/).
+
+# Don't see your car here?
+
+**openpilot can support many more cars than it currently does.** There are a few reasons your car may not be supported.
+If your car doesn't fit into any of the incompatibility criteria here, then there's a good chance it can be supported! We're adding support for new cars all the time. **We don't have a roadmap for car support**, and in fact, most car support comes from users like you!
+
+### Which cars are able to be supported?
+
+openpilot uses the existing steering, gas, and brake interfaces in your car. If your car lacks any one of these interfaces, openpilot will not be able to control the car. If your car has [ACC](https://en.wikipedia.org/wiki/Adaptive_cruise_control) and any form of [LKAS](https://en.wikipedia.org/wiki/Automated_Lane_Keeping_Systems)/[LCA](https://en.wikipedia.org/wiki/Lane_centering), then it almost certainly has these interfaces. These features generally started shipping on cars around 2016. Note that manufacturers will often make their own [marketing terms](https://en.wikipedia.org/wiki/Adaptive_cruise_control#Vehicle_models_supporting_adaptive_cruise_control) for these features, such as Hyundai's "Smart Cruise Control" branding of Adaptive Cruise Control.
+
+If your car has the following packages or features, then it's a good candidate for support.
+
+| Make | Required Package/Features |
+| ---- | ------------------------- |
+| Acura | Any car with AcuraWatch Plus will work. AcuraWatch Plus comes standard on many newer models. |
+| Ford | Any car with Lane Centering will likely work. |
+| Honda | Any car with Honda Sensing will work. Honda Sensing comes standard on many newer models. |
+| Subaru | Any car with EyeSight will work. EyeSight comes standard on many newer models. |
+| Nissan | Any car with ProPILOT will likely work. |
+| Toyota & Lexus | Any car that has Toyota/Lexus Safety Sense with "Lane Departure Alert with Steering Assist (LDA w/SA)" and/or "Lane Tracing Assist (LTA)" will work. Note that LDA without Steering Assist will not work. These features come standard on most newer models. |
+| Hyundai, Kia, & Genesis | Any car with Smart Cruise Control (SCC) and Lane Following Assist (LFA) or Lane Keeping Assist (LKAS) will work. LKAS/LFA comes standard on most newer models. Any form of SCC will work, such as NSCC. |
+| Chrysler, Jeep, & Ram | Any car with LaneSense and Adaptive Cruise Control will likely work. These come standard on many newer models. |
+
+### FlexRay
+
+All the cars that openpilot supports use a [CAN bus](https://en.wikipedia.org/wiki/CAN_bus) for communication between all the car's computers, however a CAN bus isn't the only way that the computers in your car can communicate. Most, if not all, vehicles from the following manufacturers use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) instead of a CAN bus: **BMW, Mercedes, Audi, Land Rover, and some Volvo**. These cars may one day be supported, but we have no immediate plans to support FlexRay.
+
+### Toyota Security
+
+openpilot does not yet support these Toyota models due to a new message authentication method.
+[Vote](https://comma.ai/shop#toyota-security) if you'd like to see openpilot support on these models.
+
+* Toyota RAV4 Prime 2021+
+* Toyota Sienna 2021+
+* Toyota Venza 2021+
+* Toyota Sequoia 2023+
+* Toyota Tundra 2022+
+* Toyota Highlander 2024+
+* Toyota Corolla Cross 2022+ (only US model)
+* Toyota Camry 2025+
+* Lexus NX 2022+
+* Toyota bZ4x 2023+
+* Subaru Solterra 2023+
+
diff --git a/opendbc/car/docs_definitions.py b/opendbc/car/docs/definitions.py
similarity index 100%
rename from opendbc/car/docs_definitions.py
rename to opendbc/car/docs/definitions.py
diff --git a/opendbc/car/docs.py b/opendbc/car/docs/generate_docs.py
old mode 100644
new mode 100755
similarity index 68%
rename from opendbc/car/docs.py
rename to opendbc/car/docs/generate_docs.py
index b44c873c3c..7415bbe365
--- a/opendbc/car/docs.py
+++ b/opendbc/car/docs/generate_docs.py
@@ -1,15 +1,24 @@
+#!/usr/bin/env python3
+import argparse
from collections import defaultdict
import jinja2
+import os
from enum import Enum
from natsort import natsorted
from opendbc.car import gen_empty_fingerprint
+from opendbc.car.common.basedir import BASEDIR
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarDocs, Column, CommonFootnote, PartType
+from opendbc.car.docs.definitions import CarDocs, Column, CommonFootnote, PartType
from opendbc.car.car_helpers import interfaces, get_interface_attr
from opendbc.car.values import PLATFORMS
+# FIXME: make sure we run in a sensible way if not checked out as a submodule, try-except for writing to CARS.md
+CARS_MD_OUT = os.path.join(BASEDIR, "../", "../", "../", "docs", "CARS.md")
+CARS_MD_TEMPLATE = os.path.join(BASEDIR, "docs", "CARS_template.md")
+
+
def get_all_footnotes() -> dict[Enum, int]:
all_footnotes = list(CommonFootnote)
for footnotes in get_interface_attr("Footnote", ignore_none=True).values():
@@ -57,3 +66,16 @@ def generate_cars_md(all_car_docs: list[CarDocs], template_fn: str) -> str:
group_by_make=group_by_make, footnotes=footnotes,
Column=Column)
return cars_md
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Auto generates supported cars documentation",
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+
+ parser.add_argument("--template", default=CARS_MD_TEMPLATE, help="Override default template filename")
+ parser.add_argument("--out", default=CARS_MD_OUT, help="Override default generated filename")
+ args = parser.parse_args()
+
+ with open(args.out, 'w') as f:
+ f.write(generate_cars_md(get_all_car_docs(), args.template))
+ print(f"Generated and written to {args.out}")
diff --git a/opendbc/car/tests/test_docs.py b/opendbc/car/docs/test_docs_format.py
similarity index 96%
rename from opendbc/car/tests/test_docs.py
rename to opendbc/car/docs/test_docs_format.py
index 9e6e7b632d..0a012a14cc 100644
--- a/opendbc/car/tests/test_docs.py
+++ b/opendbc/car/docs/test_docs_format.py
@@ -3,8 +3,8 @@
import re
from opendbc.car.car_helpers import interfaces
-from opendbc.car.docs import get_all_car_docs
-from opendbc.car.docs_definitions import Cable, Column, PartType, Star
+from opendbc.car.docs.generate_docs import get_all_car_docs
+from opendbc.car.docs.definitions import Cable, Column, PartType, Star
from opendbc.car.honda.values import CAR as HONDA
from opendbc.car.values import PLATFORMS
diff --git a/opendbc/car/tests/test_platform_configs.py b/opendbc/car/docs/test_platform_configs.py
similarity index 100%
rename from opendbc/car/tests/test_platform_configs.py
rename to opendbc/car/docs/test_platform_configs.py
diff --git a/opendbc/car/ford/values.py b/opendbc/car/ford/values.py
index c3cc9b5641..513c7ee86b 100644
--- a/opendbc/car/ford/values.py
+++ b/opendbc/car/ford/values.py
@@ -6,7 +6,7 @@
from panda import uds
from opendbc.car import AngleRateLimit, CarSpecs, dbc_dict, DbcDict, PlatformConfig, Platforms
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \
+from opendbc.car.docs.definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \
Device
from opendbc.car.fw_query_definitions import FwQueryConfig, LiveFwVersions, OfflineFwVersions, Request, StdQueries, p16
diff --git a/opendbc/car/gm/values.py b/opendbc/car/gm/values.py
index 852a6fbf85..59de8ffd50 100644
--- a/opendbc/car/gm/values.py
+++ b/opendbc/car/gm/values.py
@@ -2,7 +2,7 @@
from opendbc.car import dbc_dict, PlatformConfig, DbcDict, Platforms, CarSpecs
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
+from opendbc.car.docs.definitions import CarHarness, CarDocs, CarParts
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = CarParams.Ecu
diff --git a/opendbc/car/honda/values.py b/opendbc/car/honda/values.py
index 1200f69cca..a7e157d46b 100644
--- a/opendbc/car/honda/values.py
+++ b/opendbc/car/honda/values.py
@@ -4,7 +4,7 @@
from panda import uds
from opendbc.car import CarSpecs, PlatformConfig, Platforms, dbc_dict, structs
from opendbc.car.common.conversions import Conversions as CV
-from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
+from opendbc.car.docs.definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
Ecu = structs.CarParams.Ecu
diff --git a/opendbc/car/hyundai/values.py b/opendbc/car/hyundai/values.py
index 2eeb337636..3a43c5aeee 100644
--- a/opendbc/car/hyundai/values.py
+++ b/opendbc/car/hyundai/values.py
@@ -6,7 +6,7 @@
from opendbc.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
+from opendbc.car.docs.definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, p16
Ecu = CarParams.Ecu
diff --git a/opendbc/car/mazda/values.py b/opendbc/car/mazda/values.py
index 47cf43ca06..0402a1cc44 100644
--- a/opendbc/car/mazda/values.py
+++ b/opendbc/car/mazda/values.py
@@ -4,7 +4,7 @@
from opendbc.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarHarness, CarDocs, CarParts
+from opendbc.car.docs.definitions import CarHarness, CarDocs, CarParts
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = CarParams.Ecu
diff --git a/opendbc/car/nissan/values.py b/opendbc/car/nissan/values.py
index a8755e52cf..bcba1958a2 100644
--- a/opendbc/car/nissan/values.py
+++ b/opendbc/car/nissan/values.py
@@ -3,7 +3,7 @@
from panda import uds
from opendbc.car import AngleRateLimit, CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarDocs, CarHarness, CarParts
+from opendbc.car.docs.definitions import CarDocs, CarHarness, CarParts
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = CarParams.Ecu
diff --git a/opendbc/car/subaru/values.py b/opendbc/car/subaru/values.py
index c7f93f9fae..542d4e1c29 100644
--- a/opendbc/car/subaru/values.py
+++ b/opendbc/car/subaru/values.py
@@ -4,7 +4,7 @@
from panda import uds
from opendbc.car import CarSpecs, DbcDict, PlatformConfig, Platforms, dbc_dict
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Tool, Column
+from opendbc.car.docs.definitions import CarFootnote, CarHarness, CarDocs, CarParts, Tool, Column
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries, p16
Ecu = CarParams.Ecu
diff --git a/opendbc/car/tesla/values.py b/opendbc/car/tesla/values.py
index 10c8fcd066..a924b5a977 100644
--- a/opendbc/car/tesla/values.py
+++ b/opendbc/car/tesla/values.py
@@ -2,7 +2,7 @@
from opendbc.car.structs import CarParams
from opendbc.car import structs
from opendbc.car import AngleRateLimit, CarSpecs, PlatformConfig, Platforms, dbc_dict
-from opendbc.car.docs_definitions import CarDocs
+from opendbc.car.docs.definitions import CarDocs
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = CarParams.Ecu
diff --git a/opendbc/car/toyota/values.py b/opendbc/car/toyota/values.py
index a218cac0b6..f59af0c9a9 100644
--- a/opendbc/car/toyota/values.py
+++ b/opendbc/car/toyota/values.py
@@ -6,7 +6,7 @@
from opendbc.car import CarSpecs, PlatformConfig, Platforms, AngleRateLimit, dbc_dict
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car.structs import CarParams
-from opendbc.car.docs_definitions import CarFootnote, CarDocs, Column, CarParts, CarHarness
+from opendbc.car.docs.definitions import CarFootnote, CarDocs, Column, CarParts, CarHarness
from opendbc.car.fw_query_definitions import FwQueryConfig, Request, StdQueries
Ecu = CarParams.Ecu
diff --git a/opendbc/car/volkswagen/values.py b/opendbc/car/volkswagen/values.py
index c095b7767f..5f3eaf721b 100644
--- a/opendbc/car/volkswagen/values.py
+++ b/opendbc/car/volkswagen/values.py
@@ -7,7 +7,7 @@
from opendbc.car import dbc_dict, CarSpecs, DbcDict, PlatformConfig, Platforms
from opendbc.car.common.conversions import Conversions as CV
from opendbc.car import structs
-from opendbc.car.docs_definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \
+from opendbc.car.docs.definitions import CarFootnote, CarHarness, CarDocs, CarParts, Column, \
Device
from opendbc.car.fw_query_definitions import EcuAddrSubAddr, FwQueryConfig, Request, p16