diff --git a/distgit/tasks.py b/distgit/tasks.py
deleted file mode 100644
index a0efc54..0000000
--- a/distgit/tasks.py
+++ /dev/null
@@ -1,338 +0,0 @@
-import json
-from datetime import datetime
-from enum import Enum
-from json import JSONEncoder
-from random import randint
-from typing import Any
-
-from pygit2 import Oid, Repository, Signature, Tag, reference_is_valid_name
-
-# GIT_OBJECT_TAG = 4, GIT_OBJECT_TREE = 2
-GIT_OBJECT_TAG = 4
-GIT_OBJECT_TREE = 2
-EMPTY_TREE_OID_HEX = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
-
-# All task/board/comment refs live under refs/tags/ so git treats them as real
-# references for reachability (git gc, fetch, push). Without the refs/ prefix
-# pygit2 will happily create loose files outside of refs/, which git itself
-# then ignores -- making tag objects unreachable and subject to pruning.
-BOARD_REF_PREFIX: str = "refs/tags/boards/"
-TASK_REF_PREFIX: str = "refs/tags/tasks/"
-COMMENT_REF_PREFIX: str = "refs/tags/comments/"
-
-
-def _ensure_empty_tree(repo: Repository) -> None:
-	"""Ensure the well-known empty tree object exists in the repo ODB (so tags can point to it)."""
-	try:
-		repo[Oid(hex=EMPTY_TREE_OID_HEX)]
-	except KeyError:
-		repo.odb.write(GIT_OBJECT_TREE, b"tree 0\0")
-
-
-class _DateTimeEncoder(JSONEncoder):
-	def default(self, o: Any) -> Any:
-		if hasattr(o, "isoformat"):
-			return o.isoformat()
-		if isinstance(o, Enum):
-			return o.value
-		return super().default(o)
-
-
-def _tagger_str(tagger: str | Signature | None) -> str:
-	"""Produce a tagger line for git tag object (name <email> timestamp +tz). Accepts str or pygit2.Signature."""
-	if isinstance(tagger, Signature):
-		return f"{tagger.name} <{tagger.email}> {tagger.time} {tagger.offset}"
-	if isinstance(tagger, str):
-		if tagger and " <" in tagger and ">" in tagger:
-			return tagger + " 0 +0000"
-		return (tagger or "pygittools <pygittools@local>") + " 0 +0000"
-	return "pygittools <pygittools@local> 0 +0000"
-
-
-def _build_tag_raw(
-	target: Oid | str,
-	name: str,
-	tagger: str,
-	message: str,
-	object_type: str = "commit",
-) -> bytes:
-	"""Build tag object body (content only) for odb.write(4, data).
-
-	libgit2 prepends 'tag <len>\\0'. object_type must match the target (e.g. 'commit' or 'tree').
-	"""
-	hex_str = str(target) if isinstance(target, Oid) else target
-	content = f"object {hex_str}\ntype {object_type}\ntag {name}\ntagger {_tagger_str(tagger)}\n\n{message}"
-	return content.encode("utf-8")
-
-
-def _read_tag_raw(repo: Repository, oid: Oid) -> tuple[Oid, str, str, str]:
-	"""Read a tag from ODB; return (target, name, tagger_str, message)."""
-	obj = repo[oid]
-	if not isinstance(obj, Tag):
-		raise ValueError(f"Not a tag: {oid}")
-	tagger = obj.tagger
-	tagger_str = f"{tagger.name} <{tagger.email}> {tagger.time} {tagger.offset}" if tagger else ""
-	return (obj.target, obj.name, tagger_str, obj.message)
-
-
-"""
-A Task is an annotated tag (a full tag object, although it will get a ref as well).
-The usual fields of a tag (target, name, tagger) are present, but the "message" is a JSON string.
-All the fields seen below are supported, but arbitrary JSON keys may be added. Text fields are usually Markdown.
-The "name" must be a fully-qualified Git ref name (usually "refs/tags/tasks/<task-id>").
-Because the ODB is immutable, the tag will always point to the latest version of the task.
-"""
-
-
-class Task:
-	class Status(Enum):
-		TODO = "TODO"
-		IN_PROGRESS = "IN_PROGRESS"
-		IN_REVIEW = "IN_REVIEW"
-		DONE = "DONE"
-		CANCELLED = "CANCELLED"
-
-	class Priority(Enum):
-		LOW = "LOW"
-		MEDIUM = "MEDIUM"
-		HIGH = "HIGH"
-		CRITICAL = "CRITICAL"
-
-	def __init__(
-		self,
-		target: Oid | str,
-		name: str,
-		tagger: str,
-		title: str,
-		description: str = "",
-		status: Status | None = None,
-		priority: Priority | None = None,
-		assignee: str | None = None,
-		due_date: datetime | None = None,
-	):
-		if name is None:
-			name = f"{TASK_REF_PREFIX}{title.lower().replace(' ', '_')}"
-
-		if not reference_is_valid_name(name):
-			raise ValueError(f"Invalid task backend name: '{name}'")
-
-		self.target = target
-		self.tagger = tagger or ""
-		self.name = name
-		self.title = title
-		self.description = description
-		self.status = status
-		self.priority = priority
-		self.assignee = assignee
-		self.due_date = due_date
-		self.created_at = datetime.now()
-		self.comments: list[Oid | str] = []  # Comment OIDs
-		self.update_message()
-
-	def update_message(self) -> None:
-		self.updated_at = datetime.now()
-		self.message = _DateTimeEncoder().encode({
-			k: v for k, v in self.__dict__.items() if k not in ["target", "tagger", "name", "message"]
-		})
-
-	"""
-    Write the task to the repository.
-    @param repo: The repository to write to.
-    @return: The OID of the written task.
-    """
-
-	def write(self, repo: Repository) -> Oid:
-		try:
-			obj = repo[self.target]
-			object_type = (getattr(obj, "type_str", None) or "commit").lower()
-		except KeyError:
-			if str(self.target) == EMPTY_TREE_OID_HEX:
-				_ensure_empty_tree(repo)
-				object_type = "tree"
-			else:
-				object_type = "commit"
-		raw = _build_tag_raw(self.target, self.name, self.tagger, self.message, object_type=object_type)
-		oid = repo.odb.write(GIT_OBJECT_TAG, raw)
-		repo.references.create(self.name, oid, force=True)
-		return oid
-
-
-def _parse_dt(s):
-	if not s:
-		return None
-	if hasattr(s, "isoformat"):
-		return s
-	return datetime.fromisoformat(str(s).replace("Z", "+00:00")) if s else None
-
-
-def get_task(repo: Repository, ref: str) -> Task | None:
-	tag = repo.revparse_single(ref)
-	if not isinstance(tag, Tag):
-		raise ValueError(f"Requested task is not a tag: {ref}")
-	j = json.loads(tag.message)
-	status = Task.Status(j["status"]) if j.get("status") else None
-	priority = Task.Priority(j["priority"]) if j.get("priority") else None
-	t = Task(
-		tag.target,
-		tag.name,
-		str(tag.tagger),
-		j.get("title", "Untitled"),
-		description=j.get("description", ""),
-		status=status,
-		priority=priority,
-		assignee=j.get("assignee"),
-		due_date=_parse_dt(j.get("due_date")),
-	)
-	t.comments = j.get("comments", [])
-	t.created_at = _parse_dt(j.get("created_at")) or t.created_at
-	t.updated_at = _parse_dt(j.get("updated_at")) or t.updated_at
-	return t
-
-
-def get_task_by_oid(repo: Repository, oid: Oid) -> Task | None:
-	"""Load a task by its tag OID (e.g. from board.tasks)."""
-	obj = repo[oid]
-	if not isinstance(obj, Tag):
-		raise ValueError(f"Object is not a task tag: {oid}")
-	j = json.loads(obj.message)
-	status = Task.Status(j["status"]) if j.get("status") else None
-	priority = Task.Priority(j["priority"]) if j.get("priority") else None
-	t = Task(
-		obj.target,
-		obj.name,
-		str(obj.tagger),
-		j.get("title", "Untitled"),
-		description=j.get("description", ""),
-		status=status,
-		priority=priority,
-		assignee=j.get("assignee"),
-		due_date=_parse_dt(j.get("due_date")),
-	)
-	t.comments = j.get("comments", [])
-	t.created_at = _parse_dt(j.get("created_at")) or t.created_at
-	t.updated_at = _parse_dt(j.get("updated_at")) or t.updated_at
-	return t
-
-
-"""
-A Comment is an annotated tag (a full tag object) that also gets a ref under refs/tags/comments/.
-The tagger is the author of the comment and the target is the task or parent comment.
-The ref keeps the comment reachable so git gc will not prune it.
-"""
-
-
-class Comment:
-	def __init__(
-		self,
-		target: Oid,  # Task or parent comment OID
-		tagger: str,  # Author of the comment
-		content: str,
-		name: str | None = None,  # Fully-qualified ref, e.g. "refs/tags/comments/<comment-id>"
-	):
-		if name is None:
-			name = f"{COMMENT_REF_PREFIX}{randint(1, 2147483647)}"
-
-		if not reference_is_valid_name(name):
-			raise ValueError(f"Invalid comment backend name: '{name}'")
-
-		self.target = target
-		self.tagger = tagger or ""
-		self.name = name
-		self.content = content
-		self.created_at = datetime.now()
-		self.edited_at = self.created_at
-		self.update_message()
-
-	def update_message(self) -> None:
-		self.updated_at = datetime.now()
-		self.message = _DateTimeEncoder().encode({
-			k: v for k, v in self.__dict__.items() if k not in ["target", "tagger", "name", "message"]
-		})
-
-	"""
-    Write the comment to the repository.
-    @param repo: The repository to write to.
-    @return: The OID of the written comment.
-    """
-
-	def write(self, repo: Repository) -> Oid:
-		try:
-			obj = repo[self.target]
-			object_type = (getattr(obj, "type_str", None) or "commit").lower()
-		except KeyError:
-			if str(self.target) == EMPTY_TREE_OID_HEX:
-				_ensure_empty_tree(repo)
-				object_type = "tree"
-			else:
-				object_type = "commit"
-		raw = _build_tag_raw(self.target, self.name, self.tagger, self.message, object_type=object_type)
-		oid = repo.odb.write(GIT_OBJECT_TAG, raw)
-		repo.references.create(self.name, oid, force=True)
-		return oid
-
-
-def get_comment(repo: Repository, oid: Oid) -> Comment | None:
-	obj = repo[oid]
-	if not isinstance(obj, Tag):
-		raise ValueError(f"Requested comment is not a tag: {oid}")
-	j = json.loads(obj.message)
-	c = Comment(obj.target, str(obj.tagger), j["content"], obj.name)
-	c.created_at = _parse_dt(j.get("created_at")) or c.created_at
-	c.edited_at = _parse_dt(j.get("edited_at"))
-	return c
-
-
-"""
-A Board is an annotated tag (a full tag object, although it will get a ref as well).
-The only used field is "message" which is a JSON string.
-The "tasks" field is an array of task OIDs.
-"""
-
-
-class Board:
-	def __init__(
-		self,
-		target: Oid | str,
-		name: str,
-		tagger: str,
-		description: str = "",
-	):
-		self.target = target
-		self.name = name
-		self.tagger = tagger or ""
-		self.description = description
-		self.created_at = datetime.now()
-		self.updated_at = datetime.now()
-		self.tasks: list[Oid | str] = []  # Task OIDs
-		self.update_message()
-
-	def update_message(self) -> None:
-		self.updated_at = datetime.now()
-		self.message = _DateTimeEncoder().encode({
-			k: v for k, v in self.__dict__.items() if k not in ["target", "name", "tagger", "message"]
-		})
-
-	def write(self, repo: Repository) -> Oid:
-		if str(self.target) == EMPTY_TREE_OID_HEX:
-			_ensure_empty_tree(repo)
-		try:
-			obj = repo[self.target]
-			object_type = (getattr(obj, "type_str", None) or "commit").lower()
-		except KeyError:
-			object_type = "tree" if str(self.target) == EMPTY_TREE_OID_HEX else "commit"
-		raw = _build_tag_raw(self.target, self.name, self.tagger, self.message, object_type=object_type)
-		oid = repo.odb.write(GIT_OBJECT_TAG, raw)
-		repo.references.create(self.name, oid, force=True)
-		return oid
-
-
-def get_board(repo: Repository, ref: str) -> Board | None:
-	tag = repo.revparse_single(ref)
-	if not isinstance(tag, Tag):
-		return None
-	j = json.loads(tag.message)
-	b = Board(tag.target, tag.name, str(tag.tagger), j.get("description", ""))
-	b.tasks = j.get("tasks", [])
-	b.created_at = _parse_dt(j.get("created_at")) or b.created_at
-	b.updated_at = _parse_dt(j.get("updated_at")) or b.updated_at
-	return b
diff --git a/pygitweb/config.py b/pygitweb/config.py
index d0dfb88..1754c05 100644
--- a/pygitweb/config.py
+++ b/pygitweb/config.py
@@ -110,10 +110,13 @@ OIDCProvider: TODO - Will authenticate against an OIDC server.
 MatrixProvider: TODO - Will authenticate against the Matrix network.
 LDAPProvider: TODO - Will authenticate against a local LDAP server.
 """
-DISTGIT_AUTH: str | None = os.getenv("DISTGIT_AUTH", None)
-DISTGIT_ADMIN_USER: str | None = os.getenv("DISTGIT_ADMIN_USER", None)
-DISTGIT_ADMIN_PASSWORD: str | None = os.getenv("DISTGIT_ADMIN_PASSWORD", None)
-DISTGIT_SESSION_TIMEOUT: str | int | None = os.getenv("DISTGIT_SESSION_TIMEOUT", 3600 * 24 * 7)
+PYGITWEB_AUTH: str | None = os.getenv("PYGITWEB_AUTH", None)
+if PYGITWEB_AUTH == "None":
+	PYGITWEB_AUTH = None
+
+PYGITWEB_ADMIN_USER: str | None = os.getenv("PYGITWEB_ADMIN_USER", None)
+PYGITWEB_ADMIN_PASSWORD: str | None = os.getenv("PYGITWEB_ADMIN_PASSWORD", None)
+PYGITWEB_SESSION_TIMEOUT: str | int | None = os.getenv("PYGITWEB_SESSION_TIMEOUT", 3600 * 24 * 7)
 
 # Config file paths (can be overridden by env)
 GITWEB_CONFIG: str | None = os.getenv("GITWEB_CONFIG", None)
diff --git a/pygitweb/main.py b/pygitweb/main.py
index 837c67e..6b64818 100644
--- a/pygitweb/main.py
+++ b/pygitweb/main.py
@@ -42,12 +42,12 @@ from pygitweb.actions import (
 )
 from pygitweb.config import (
 	ACTIONS,
-	DISTGIT_ADMIN_PASSWORD,
-	DISTGIT_ADMIN_USER,
-	DISTGIT_AUTH,
-	DISTGIT_SESSION_TIMEOUT,
 	EXPORT_OK,
 	PROJECTROOT,
+	PYGITWEB_ADMIN_PASSWORD,
+	PYGITWEB_ADMIN_USER,
+	PYGITWEB_AUTH,
+	PYGITWEB_SESSION_TIMEOUT,
 	STRICT_EXPORT,
 	check_loadavg,
 	configure_gitweb_features,
@@ -80,7 +80,7 @@ with open("pygitweb/README.md", encoding="utf-8") as _readme_file:
 	_app_description = _readme_file.read()
 
 app = FastAPI(
-	debug=(DISTGIT_AUTH is None),
+	debug=(PYGITWEB_AUTH is None),
 	title="PyGitWeb",
 	summary="FastAPI + Pygit2 Repo Browser",
 	description=_app_description,
@@ -106,20 +106,20 @@ _auth_provider = None
 
 
 def _get_auth_provider():
-	"""Return auth provider instance if DISTGIT_AUTH is set; None if auth disabled."""
+	"""Return auth provider instance if PYGITWEB_AUTH is set; None if auth disabled."""
 	global _auth_provider
-	if DISTGIT_AUTH == "None":
+	if not PYGITWEB_AUTH:
 		return None
 	if _auth_provider is not None:
 		return _auth_provider
 	try:
-		mod_name, _, cls_name = DISTGIT_AUTH.rpartition(".")
+		mod_name, _, cls_name = PYGITWEB_AUTH.rpartition(".")
 		mod = __import__(mod_name, fromlist=[cls_name])
 		cls = getattr(mod, cls_name)
 		# RootAuthProvider accepts admin_user, admin_password, session_timeout from env
-		admin_user = DISTGIT_ADMIN_USER.encode("utf-8") or None
-		admin_password = DISTGIT_ADMIN_PASSWORD.encode("utf-8") or None
-		timeout = DISTGIT_SESSION_TIMEOUT
+		admin_user = PYGITWEB_ADMIN_USER.encode("utf-8") or None
+		admin_password = PYGITWEB_ADMIN_PASSWORD.encode("utf-8") or None
+		timeout = PYGITWEB_SESSION_TIMEOUT
 		_auth_provider = cls(
 			admin_user=admin_user,
 			admin_password=admin_password,
@@ -132,7 +132,7 @@ def _get_auth_provider():
 
 def _request_can_add_project(request: Request) -> bool:
 	"""True if auth is disabled or request has a valid session (header X-Session-Token or param/cookie session)."""
-	if DISTGIT_AUTH == "None":
+	if not PYGITWEB_AUTH:
 		return True
 	provider = _get_auth_provider()
 	if provider is None:
@@ -211,8 +211,6 @@ def git_project_list(
 	if not list_:
 		raise HTTPException(status_code=404, detail="No projects found")
 
-	auth_disabled = DISTGIT_AUTH == "None"
-
 	def board_cell(pr: dict) -> str:
 		path = pr.get("path", "")
 		path_enc = quote(path, safe="/")
@@ -223,7 +221,7 @@ def git_project_list(
 			has_boards = False
 		if has_boards:
 			return f'<a href="/project/{path_enc}/board/">project board</a>'
-		grey_style = ' style="color: #999; cursor: not-allowed;"' if not auth_disabled else ""
+		grey_style = ' style="color: #999; cursor: not-allowed;"' if PYGITWEB_AUTH else ""
 		proj_q = quote(path, safe="")
 		return f'<a href="/board/create?project={proj_q}"{grey_style}>create board</a>'
 
@@ -286,7 +284,7 @@ def board_create_page(
 	project: Annotated[str | None, Query(alias="p")] = None,
 ) -> RedirectResponse:
 	"""Create a board named 'Tasks' (refs/tags/boards/Tasks) and redirect to the project summary."""
-	if DISTGIT_AUTH != "None":
+	if PYGITWEB_AUTH:
 		raise HTTPException(status_code=401, detail="Authentication required")
 	p = project or request.query_params.get("project")
 	if not p:
diff --git a/pygitweb/tasks.py b/pygitweb/tasks.py
index dc43048..a854baf 100644
--- a/pygitweb/tasks.py
+++ b/pygitweb/tasks.py
@@ -23,7 +23,7 @@ from pygittools.tasks import (
 	get_task,
 	get_task_by_oid,
 )
-from pygitweb.config import DISTGIT_AUTH, EXPORT_OK, PROJECTROOT, STRICT_EXPORT
+from pygitweb.config import EXPORT_OK, PROJECTROOT, PYGITWEB_AUTH, STRICT_EXPORT
 from pygitweb.git_helpers import git_get_references, open_repo
 from pygitweb.projects import git_get_projects_list
 from pygitweb.validation import is_valid_project
@@ -34,7 +34,7 @@ EMPTY_TREE_OID = pygit2.Oid(hex="4b825dc642cb6eb9a060e54bf8d69288fbee4904")
 
 def _require_auth_disabled() -> None:
 	"""Raise 401 if authentication is enabled (we will add real auth later)."""
-	if DISTGIT_AUTH != "None":
+	if PYGITWEB_AUTH:
 		raise HTTPException(status_code=401, detail="Authentication required")
 
 
