feat: initial public release

ConsentOS — a privacy-first cookie consent management platform.

Self-hosted, source-available alternative to OneTrust, Cookiebot, and
CookieYes. Full standards coverage (IAB TCF v2.2, GPP v1, Google
Consent Mode v2, GPC, Shopify Customer Privacy API), multi-tenant
architecture with role-based access, configuration cascade
(system → org → group → site → region), dark-pattern detection in
the scanner, and a tamper-evident consent record audit trail.

This is the initial public release. Prior development history is
retained internally.

See README.md for the feature list, architecture overview, and
quick-start instructions. Licensed under the Elastic Licence 2.0 —
self-host freely; do not resell as a managed service.
This commit is contained in:
James Cottrill
2026-04-13 14:20:15 +00:00
commit fbf26453f2
341 changed files with 62807 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
import uuid
from sqlalchemy import ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
from src.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin
class CookieCategory(UUIDPrimaryKeyMixin, TimestampMixin, Base):
"""Cookie category taxonomy (necessary, functional, analytics, marketing, personalisation)."""
__tablename__ = "cookie_categories"
name: Mapped[str] = mapped_column(String(50), unique=True, nullable=False)
slug: Mapped[str] = mapped_column(String(50), unique=True, nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
is_essential: Mapped[bool] = mapped_column(default=False, nullable=False)
display_order: Mapped[int] = mapped_column(Integer, server_default="0", nullable=False)
# TCF purpose mapping
tcf_purpose_ids: Mapped[list | None] = mapped_column(JSONB, nullable=True)
# Google Consent Mode consent type mapping
gcm_consent_types: Mapped[list | None] = mapped_column(JSONB, nullable=True)
# Relationships
cookies: Mapped[list["Cookie"]] = relationship(back_populates="category")
allow_list_entries: Mapped[list["CookieAllowListEntry"]] = relationship(
back_populates="category"
)
class Cookie(UUIDPrimaryKeyMixin, TimestampMixin, Base):
"""A cookie discovered on a site via scanning or client-side reporting."""
__tablename__ = "cookies"
__table_args__ = (
UniqueConstraint(
"site_id",
"name",
"domain",
"storage_type",
name="uq_cookies_site_name_domain_type",
),
)
site_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("sites.id", ondelete="CASCADE"),
nullable=False,
index=True,
)
category_id: Mapped[uuid.UUID | None] = mapped_column(
UUID(as_uuid=True),
ForeignKey("cookie_categories.id", ondelete="SET NULL"),
nullable=True,
index=True,
)
name: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
domain: Mapped[str] = mapped_column(String(255), nullable=False)
storage_type: Mapped[str] = mapped_column(String(30), server_default="cookie", nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
vendor: Mapped[str | None] = mapped_column(String(255), nullable=True)
path: Mapped[str | None] = mapped_column(String(500), nullable=True)
max_age_seconds: Mapped[int | None] = mapped_column(Integer, nullable=True)
is_http_only: Mapped[bool | None] = mapped_column(nullable=True)
is_secure: Mapped[bool | None] = mapped_column(nullable=True)
same_site: Mapped[str | None] = mapped_column(String(10), nullable=True)
review_status: Mapped[str] = mapped_column(String(20), server_default="pending", nullable=False)
first_seen_at: Mapped[str | None] = mapped_column(String(50), nullable=True)
last_seen_at: Mapped[str | None] = mapped_column(String(50), nullable=True)
# Relationships
site: Mapped["Site"] = relationship(back_populates="cookies") # noqa: F821
category: Mapped["CookieCategory | None"] = relationship(back_populates="cookies")
class CookieAllowListEntry(UUIDPrimaryKeyMixin, TimestampMixin, Base):
"""Approved cookies per site with category assignment."""
__tablename__ = "cookie_allow_list"
__table_args__ = (
UniqueConstraint(
"site_id",
"name_pattern",
"domain_pattern",
name="uq_allow_list_site_name_domain",
),
)
site_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("sites.id", ondelete="CASCADE"),
nullable=False,
index=True,
)
category_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("cookie_categories.id", ondelete="RESTRICT"),
nullable=False,
)
name_pattern: Mapped[str] = mapped_column(String(255), nullable=False)
domain_pattern: Mapped[str] = mapped_column(String(255), nullable=False)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
# Relationships
site: Mapped["Site"] = relationship(back_populates="cookie_allow_list") # noqa: F821
category: Mapped["CookieCategory"] = relationship(back_populates="allow_list_entries")
class KnownCookie(UUIDPrimaryKeyMixin, TimestampMixin, Base):
"""Shared knowledge base of known cookie patterns for auto-categorisation."""
__tablename__ = "known_cookies"
__table_args__ = (
UniqueConstraint("name_pattern", "domain_pattern", name="uq_known_cookies_name_domain"),
)
name_pattern: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
domain_pattern: Mapped[str] = mapped_column(String(255), nullable=False)
category_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("cookie_categories.id", ondelete="RESTRICT"),
nullable=False,
)
vendor: Mapped[str | None] = mapped_column(String(255), nullable=True)
description: Mapped[str | None] = mapped_column(Text, nullable=True)
is_regex: Mapped[bool] = mapped_column(default=False, nullable=False)