This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Version Control Best Practices
Loading…
Version Control Best Practices
Relevant source files
Purpose and Scope
This document explains the version control practices and configurations implemented in this repository to ensure secure handling of secrets and maintain a clean Git history. It focuses on the .gitignore configuration, the template-based approach for sensitive configuration, and recommended workflows for committing changes safely.
For information about the automated CI/CD workflows themselves, see Continuous Integration and Documentation Pipeline. For general configuration management, see Configuration Reference.
File Classification Strategy
The repository implements a strict separation between code/documentation (version controlled) and secrets/runtime data (explicitly excluded). This classification prevents accidental exposure of sensitive information.
graph TB
subgraph VersionControlled["Version Controlled (Committed to Git)"]
DockerCompose["docker-compose.yml\nService orchestration"]
MosquittoConf["mosquitto.conf\nBroker configuration"]
EnvSample[".env.sample\nSecret template"]
Workflows["GitHub Actions workflows\n.github/workflows/*"]
README["README.md\nDocumentation"]
Gitignore[".gitignore\nExclusion rules"]
end
subgraph Ignored["Explicitly Ignored (Never Committed)"]
EnvFile[".env\nContains CLOUDFLARE_TUNNEL_TOKEN"]
DataDir["data/*\nRuntime broker data"]
end
subgraph Runtime["Runtime Environment"]
DockerEngine["Docker Engine"]
CloudflaredContainer["cloudflared container"]
MosquittoContainer["mosquitto container"]
end
DockerCompose -->|orchestrates| DockerEngine
MosquittoConf -->|mounted into| MosquittoContainer
EnvSample -->|developer copies to| EnvFile
Gitignore -->|excludes| EnvFile
Gitignore -->|excludes| DataDir
EnvFile -->|loaded by docker-compose| CloudflaredContainer
DataDir -->|mounted into| MosquittoContainer
File Classification Diagram
Sources : .gitignore:1-2 .env.sample1 docker-compose.yml
The .gitignore Configuration
The .gitignore file serves as the primary security boundary, preventing sensitive files from being committed to version control.
Current Exclusions
| Pattern | Purpose | Risk if Committed |
|---|---|---|
.env | Environment variables containing CLOUDFLARE_TUNNEL_TOKEN | Exposes tunnel access credentials, allowing unauthorized traffic routing |
data/* | Mosquitto runtime data (messages, persistence files) | May contain sensitive message content, creates unnecessary repository bloat |
Sources : .gitignore:1-2
Security Implications
The .env file exclusion is critical because it contains the CLOUDFLARE_TUNNEL_TOKEN value. This token provides authentication to route traffic through your Cloudflare Tunnel. If committed to a public repository (or even a private one with multiple collaborators), the token could be:
- Extracted from Git history - Even if deleted in a later commit, the token remains in Git history
- Used to route unauthorized traffic - An attacker with the token can send traffic through your tunnel
- Difficult to rotate - Revoking and regenerating the token requires reconfiguring the entire Cloudflare Tunnel
Sources : .env.sample1 .gitignore1
Template-Based Secret Management
The repository uses a template file pattern to document required secrets without exposing actual values.
sequenceDiagram
participant Dev as "Developer"
participant Repo as "Git Repository"
participant Local as "Local Filesystem"
participant Docker as "Docker Compose"
participant CF as "Cloudflare API"
Note over Repo: .env.sample checked into Git\nNote over Repo: .gitignore excludes .env
Dev->>Repo: git clone repository
Repo->>Local: .env.sample available
Repo->>Local: .gitignore rules applied
Dev->>CF: Create tunnel via dashboard
CF-->>Dev: Receive CLOUDFLARE_TUNNEL_TOKEN
Dev->>Local: cp .env.sample .env
Dev->>Local: Edit .env with actual token
Note over Local: .env now contains secret
Dev->>Docker: docker-compose up
Docker->>Local: Read .env file
Local-->>Docker: CLOUDFLARE_TUNNEL_TOKEN value
Docker->>Docker: Inject into cloudflared container
Note over Dev,Local: .env never committed due to .gitignore
Template File Flow
Sources : .env.sample1 .gitignore1
The .env.sample Template
The .env.sample file serves three purposes:
- Documentation - Shows developers what environment variables are required
- Template - Provides copy-paste structure for creating the actual
.envfile - Placeholder Values - Uses obvious placeholder text (
your_token) that will fail if not replaced
Content:
CLOUDFLARE_TUNNEL_TOKEN=your_token
This template is intentionally minimal. The placeholder value your_token will cause the tunnel connection to fail, forcing developers to replace it with a real token from the Cloudflare dashboard.
Sources : .env.sample1
Protected Files and Their Purposes
Files That Are Safe to Commit
| File | Contains Secrets? | Why It’s Safe |
|---|---|---|
docker-compose.yml | No | References environment variable by name (${CLOUDFLARE_TUNNEL_TOKEN}), not value |
mosquitto.conf | No | Only contains port numbers and protocol settings |
.env.sample | No | Contains placeholder text, not real secrets |
README.md | No | Documentation only |
Sources : .env.sample1 docker-compose.yml mosquitto.conf
Files That Must Never Be Committed
| File/Directory | Contains Secrets? | Contents |
|---|---|---|
.env | YES | Real CLOUDFLARE_TUNNEL_TOKEN value |
data/* | Potentially | MQTT message persistence, retained messages, subscription state |
Sources : .gitignore:1-2
Git Workflow Best Practices
Pre-Commit Verification Process
Sources : .gitignore:1-2
Recommended Workflow Steps
-
Initial Setup (One-time per developer)
- Clone repository:
git clone <repository-url> - Verify
.gitignoreexists:cat .gitignore - Copy template:
cp .env.sample .env - Populate
.envwith real token from Cloudflare dashboard - Verify exclusion:
git statusshould not show.env
- Clone repository:
-
Before Each Commit
- Review staged files:
git status - Confirm
.envis NOT in the staged files list - Confirm
data/*is NOT in the staged files list - If either appears, investigate why (usually means
.gitignoreis missing or misconfigured)
- Review staged files:
-
After Cloning on a New Machine
- The
.envfile will not exist (this is correct) - Must manually create
.envfrom.env.sampletemplate - Must obtain new token from Cloudflare or securely transfer existing token
- The
Sources : .gitignore:1-2 .env.sample1
Verification Commands
Checking Gitignore Effectiveness
Sources : .gitignore1
Staged Files Safety Check
Before committing, run this command to list all files that will be committed:
This output should never include:
.env- Any file under
data/
If either appears, do not commit. Instead:
Sources : .gitignore:1-2
sequenceDiagram
participant Admin as "Administrator"
participant CF as "Cloudflare Dashboard"
participant Local as "Local .env File"
participant Docker as "Docker Environment"
participant Git as "Git Repository"
Note over Admin,Git: Token Rotation Process
Admin->>CF: Regenerate tunnel token
CF-->>Admin: New CLOUDFLARE_TUNNEL_TOKEN
Admin->>Local: Update .env with new token
Note over Local: Old token still in file
Admin->>Docker: docker-compose down
Admin->>Docker: docker-compose up
Docker->>Local: Read updated .env
Note over Docker: New token now active
Note over Admin,Git: Verification
Admin->>Git: git status
Git-->>Admin: .env not in changes
Note over Admin: Confirm .gitignore still protecting .env
Admin->>CF: Verify tunnel connected
CF-->>Admin: Tunnel active with new token
Secret Rotation Procedure
When the CLOUDFLARE_TUNNEL_TOKEN needs to be rotated (compromised token, security policy, or regular rotation):
Rotation Flow Diagram
Sources : .env.sample1 .gitignore1
Step-by-Step Rotation
- Generate new token in Cloudflare Dashboard (Networks → Tunnels → Select tunnel → Configure)
- Update local
.envfile with newCLOUDFLARE_TUNNEL_TOKENvalue - Restart services :
docker-compose down && docker-compose up -d - Verify exclusion : Run
git statusto confirm.envis not staged - Test connection : Verify tunnel connects with new token
- Revoke old token (optional, if Cloudflare UI supports it)
Important : The .env file is never committed during this process. The token change remains local to each deployment environment.
Sources : .env.sample1 .gitignore1
Common Mistakes to Avoid
Mistake 1: Committing .env Before Adding .gitignore
Scenario : Developer commits .env file before creating .gitignore.
Result : Token is now in Git history permanently.
Prevention :
- Ensure
.gitignoreexists before first commit - This repository includes
.gitignorefrom initial commit
Recovery (if it happens):
Sources : .gitignore1
Mistake 2: Using .env.sample Directly
Scenario : Developer renames .env.sample to .env instead of copying.
Result : The template file disappears from version control.
Prevention :
- Always use
cp .env.sample .env(copy, not move) - Never run
git rm .env.sample
Sources : .env.sample1
Mistake 3: Accidentally Staging with git add -A
Scenario : Running git add -A or git add . without reviewing files.
Result : Might stage .env if .gitignore is misconfigured or missing.
Prevention :
- Always run
git statusbeforegit add - Use
git add <specific-files>for granular control - Review
git diff --cachedbefore committing
Sources : .gitignore1
Integration with CI/CD
The version control practices integrate with the automated CI/CD pipelines:
CI Pipeline Behavior
The GitHub Actions CI workflow (.github/workflows/ci.yml) does not require the .env file because:
- It only tests the
mosquittoservice, notcloudflared - The Mosquitto broker does not require the
CLOUDFLARE_TUNNEL_TOKEN - Secrets would be injected via GitHub Secrets for full-stack testing (not implemented in this repository)
Sources : .github/workflows/ci.yml docker-compose.yml
Documentation Pipeline
The documentation build workflow (.github/workflows/build-docs.yml) operates on the committed files only:
- Reads
.env.sample(committed) to document the template format - Never accesses
.env(ignored) as it’s not present in the repository - Builds static documentation without requiring runtime secrets
Sources : .github/workflows/build-docs.yml .env.sample1
Production Environment Considerations
For production deployments, additional secret management practices should be considered:
Production Secret Management Table
| Environment | Secret Storage | Injection Method | Rotation Strategy |
|---|---|---|---|
| Local Development | .env file | docker-compose reads file | Manual edit + restart |
| CI/CD | GitHub Secrets | Environment variables in workflow | Update in GitHub UI |
| Production | Secrets manager (AWS Secrets Manager, HashiCorp Vault, etc.) | Environment variables via orchestration | Automated rotation with zero-downtime |
For production deployments beyond local development, consider:
- Secrets Manager Integration - Replace
.envfile with calls to a secrets management service - Automated Rotation - Implement automatic token rotation with zero-downtime restarts
- Audit Logging - Track when secrets are accessed and by whom
- Encryption at Rest - Ensure secrets are encrypted when stored on disk
Sources : .env.sample1
Summary of Key Practices
- Never commit
.env- The.gitignoreconfiguration prevents this - Always use
.env.sampleas template - Copy, don’t move - Verify before each commit - Run
git statusand check for excluded files - Rotate tokens out-of-band - Token updates never touch Git
- Audit Git history - Ensure secrets never entered version control
These practices implement the principle of “separation of code and configuration,” where sensitive credentials are injected at runtime rather than embedded in version-controlled artifacts.
Sources : .gitignore:1-2 .env.sample1
Dismiss
Refresh this wiki
Enter email to refresh