mirror of
https://github.com/Anon-Planet/thgtoa.git
synced 2026-05-06 11:34:18 +02:00
Compare commits
64 Commits
v2.0.0-dev
...
4c3ca7bfd7
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c3ca7bfd7 | |||
| 5636291c8a | |||
| 41ac52de0a | |||
| f100633632 | |||
| 062128732e | |||
| e0d16797ed | |||
| d5659af3f7 | |||
| a2dbdd10e9 | |||
| cd00fa79fd | |||
| c49cc87390 | |||
| a14191bc7b | |||
| a47e02939d | |||
| 52a38f5deb | |||
| 206a6ff6b7 | |||
| 0e8de6ccc0 | |||
| d0dfec95db | |||
| 783f02f404 | |||
| 6c8dba5d5f | |||
| ee32450516 | |||
| c7452ea796 | |||
| 3621967517 | |||
| 88896a4f15 | |||
| 14de26d77d | |||
| 3b430dc96a | |||
| 565f3b8516 | |||
| fd60ef8460 | |||
| fb5e1fca74 | |||
| 02764539f2 | |||
| 2e0b7a9716 | |||
| 239d1c632f | |||
| c76ccd3e43 | |||
| 37068765cc | |||
| 2c3dea5f41 | |||
| d623dda610 | |||
| 8e386addb8 | |||
| 7b3599df63 | |||
| ac870b1497 | |||
| 88cee5a3c0 | |||
| 56678b3567 | |||
| 3b1c5946ae | |||
| 6448e2b786 | |||
| 957da7e2af | |||
| 6c4fd4a5c0 | |||
| 2f5254a9fb | |||
| 23f2acb788 | |||
| 2bfa84de28 | |||
| 9b98497ed3 | |||
| d212e86012 | |||
| 621a6ae3d0 | |||
| 377e8ecad7 | |||
| 2c35d7ced8 | |||
| 6caa68cca6 | |||
| 516eeb7177 | |||
| 500c3baa7f | |||
| 7dd6442f20 | |||
| 580a499841 | |||
| dc786a2bcd | |||
| 75568fc90f | |||
| 7cc0ba2a1b | |||
| 1a934baede | |||
| dadade4cc1 | |||
| 041e2937aa | |||
| b8c265c929 | |||
| 11b6004bba |
@@ -0,0 +1,137 @@
|
||||
name: 📖 Build PDF
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "mkdocs.yml"
|
||||
- "scripts/build_guide_pdf.py"
|
||||
- ".github/workflows/build-pdf.yml"
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "mkdocs.yml"
|
||||
- "scripts/build_guide_pdf.py"
|
||||
- ".github/workflows/build-pdf.yml"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
pdf:
|
||||
name: PDF build and sign
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 🛠️ Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🐍 Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
|
||||
- name: 📦 Install MkDocs Material
|
||||
run: pip install mkdocs-material
|
||||
|
||||
- name: 🌐 Install Chromium
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --no-install-recommends chromium
|
||||
|
||||
- name: 🔑 Install GPG tools
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gnupg
|
||||
|
||||
- name: 🖨️ Build PDF
|
||||
env:
|
||||
CI: true
|
||||
run: python scripts/build_guide_pdf.py --both
|
||||
|
||||
- name: 🔢 Generate SHA256 hashes for root directory
|
||||
run: |
|
||||
cd ${{ github.workspace }}
|
||||
sha256sum export/thgtoa.pdf > sha256sum-light.txt
|
||||
sha256sum export/thgtoa-dark.pdf >> sha256sum-light.txt
|
||||
|
||||
# Create separate hash files for each PDF
|
||||
sha256sum export/thgtoa.pdf > thgtoa.pdf.sha256
|
||||
sha256sum export/thgtoa-dark.pdf > thgtoa-dark.pdf.sha256
|
||||
|
||||
- name: 🔒 Sign PDFs with GPG key
|
||||
env:
|
||||
GPG_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
cd ${{ github.workspace }}
|
||||
|
||||
# Import GPG key
|
||||
echo "$GPG_KEY" | gpg --batch --import 2>/dev/null || true
|
||||
|
||||
# Sign the PDFs (detach signature)
|
||||
gpg --batch --yes --armor --detach-sign --output export/thgtoa.pdf.sig export/thgtoa.pdf
|
||||
gpg --batch --yes --armor --detach-sign --output export/thgtoa-dark.pdf.sig export/thgtoa-dark.pdf
|
||||
|
||||
# Also sign the hash files
|
||||
gpg --batch --yes --armor --detach-sign --output sha256sum-light.txt.sig sha256sum-light.txt
|
||||
|
||||
upload:
|
||||
name: Upload artifacts
|
||||
runs-on: ubuntu-latest
|
||||
needs: pdf
|
||||
steps:
|
||||
- name: 📤 Upload PDF artifact (Light Mode)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: light-pdf-files
|
||||
path: |
|
||||
export/thgtoa.pdf
|
||||
export/thgtoa.pdf.sig
|
||||
thgtoa.pdf.sha256
|
||||
archive: false
|
||||
if-no-files-found: error
|
||||
retention-days: 90
|
||||
|
||||
- name: 📤 Upload PDF artifact (Dark Mode)
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: dark-pdf-files
|
||||
path: |
|
||||
export/thgtoa-dark.pdf
|
||||
export/thgtoa-dark.pdf.sig
|
||||
thgtoa-dark.pdf.sha256
|
||||
archive: false
|
||||
if-no-files-found: error
|
||||
retention-days: 90
|
||||
|
||||
- name: 📤 Upload combined hash file to root
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: hash-files
|
||||
path: |
|
||||
sha256sum-light.txt
|
||||
archive: false
|
||||
if-no-files-found: error
|
||||
retention-days: 90
|
||||
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: pdf
|
||||
steps:
|
||||
- name: 🚀 Create Release (if tag exists)
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
export/thgtoa.pdf
|
||||
export/thgtoa-dark.pdf
|
||||
export/thgtoa.pdf.sig
|
||||
export/thgtoa-dark.pdf.sig
|
||||
sha256sum-light.txt
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -0,0 +1,20 @@
|
||||
name: 🚀 Publish docs via GitHub Pages
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Publish the Hitchhiker's Guide
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout main
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Deploy docs
|
||||
uses: mhausenblas/mkdocs-deploy-gh-pages@master
|
||||
# Or use mhausenblas/mkdocs-deploy-gh-pages@nomaterial to build without the mkdocs-material theme
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
CUSTOM_DOMAIN: anonymousplanet.org
|
||||
@@ -0,0 +1,77 @@
|
||||
name: '🦠 VirusTotal Scan'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: '📦 Checkout'
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: '🔍 Download PDF artifacts from build workflow'
|
||||
uses: actions/download-artifact@v7
|
||||
with:
|
||||
pattern: light-pdf-files,dark-pdf-files
|
||||
path: pdfs
|
||||
merge-multiple: true
|
||||
|
||||
- name: '🦠 Scan PDF files using VT'
|
||||
uses: crazy-max/ghaction-virustotal@v5
|
||||
with:
|
||||
vt_api_key: ${{ secrets.VT_API_KEY }}
|
||||
update_release_body: false # We'll handle this manually in the next step
|
||||
files: |
|
||||
./pdfs/thgtoa.pdf
|
||||
./pdfs/thgtoa-dark.pdf
|
||||
|
||||
- name: '📊 Extract VT scan results'
|
||||
id: vt-scan
|
||||
run: |
|
||||
echo "scan completed" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: '🔗 Generate VT report links'
|
||||
run: |
|
||||
# Create a markdown file with VT scan results and links
|
||||
cat > vt-results.md << EOF
|
||||
## VirusTotal Scan Results
|
||||
|
||||
**Scan Date:** $(date -u +"%Y-%m-%d %H:%M UTC")
|
||||
|
||||
### thgtoa.pdf (Light Mode)
|
||||
- **VT Report:** https://www.virustotal.com/gui/file/$(sha256sum pdfs/thgtoa.pdf | cut -d' ' -f1)
|
||||
|
||||
### thgtoa-dark.pdf (Dark Mode)
|
||||
- **VT Report:** https://www.virustotal.com/gui/file/$(sha256sum pdfs/thgtoa-dark.pdf | cut -d' ' -f1)
|
||||
|
||||
---
|
||||
*Scan performed automatically by GitHub Actions*
|
||||
EOF
|
||||
|
||||
- name: '📝 Update release with VT results (if tag exists)'
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
run: |
|
||||
# Get the latest release notes
|
||||
RELEASE_NOTES=$(gh release view ${{ github.ref_name }} --json body --jq .body 2>/dev/null || echo "")
|
||||
|
||||
# Append VT results to release notes
|
||||
if [ -n "$RELEASE_NOTES" ]; then
|
||||
echo "" >> vt-results.md
|
||||
echo "---" >> vt-results.md
|
||||
echo "### Previous Release Notes" >> vt-results.md
|
||||
echo "$RELEASE_NOTES" >> vt-results.md
|
||||
fi
|
||||
|
||||
# Update the release with VT results
|
||||
gh release edit ${{ github.ref_name }} --notes-file vt-results.md
|
||||
+18
-121
@@ -1,130 +1,27 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
# Visual Studio (Windows) solution metadata
|
||||
.vs/
|
||||
.vscode/
|
||||
|
||||
# Python (MkDocs, scripts/build_guide_pdf.py)
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
.env
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
# Cache
|
||||
.cache/
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
# MkDocs build output and local PDF export
|
||||
site/
|
||||
_site/
|
||||
_site_test/
|
||||
build/
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
# Export directory - but track hash files and signatures
|
||||
export/thgtoa.pdf.sha256
|
||||
export/thgtoa-dark.pdf.sha256
|
||||
*.sig
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
---
|
||||
# Documentation:
|
||||
# https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md
|
||||
|
||||
# Default state for all rules
|
||||
default: false
|
||||
|
||||
# MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time
|
||||
MD001: true
|
||||
|
||||
# MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading
|
||||
MD002:
|
||||
# Heading level
|
||||
level: 1
|
||||
|
||||
# MD003/heading-style/header-style - Heading style
|
||||
|
||||
MD003:
|
||||
# Heading style
|
||||
# # ATX style H1
|
||||
style: "atx"
|
||||
|
||||
# MD004/ul-style - Unordered list style
|
||||
MD004:
|
||||
# List style
|
||||
style: "sublist"
|
||||
|
||||
# MD005/list-indent - Inconsistent indentation for list items at the same level
|
||||
MD005: true
|
||||
|
||||
# MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line
|
||||
MD006: true
|
||||
|
||||
# MD007/ul-indent - Unordered list indentation
|
||||
MD007:
|
||||
# Spaces for indent
|
||||
indent: 4
|
||||
# Whether to indent the first level of the list
|
||||
start_indented: false
|
||||
|
||||
# MD009/no-trailing-spaces - Trailing spaces
|
||||
MD009:
|
||||
# Spaces for line break
|
||||
br_spaces: 2
|
||||
# Allow spaces for empty lines in list items
|
||||
list_item_empty_lines: false
|
||||
# Include unnecessary breaks
|
||||
strict: false
|
||||
|
||||
# MD010/no-hard-tabs - Hard tabs
|
||||
MD010:
|
||||
# Include code blocks
|
||||
code_blocks: false
|
||||
# Number of spaces for each hard tab
|
||||
spaces_per_tab: 1
|
||||
|
||||
# MD011/no-reversed-links - Reversed link syntax
|
||||
MD011: true
|
||||
|
||||
# MD012/no-multiple-blanks - Multiple consecutive blank lines
|
||||
MD012:
|
||||
# Consecutive blank lines
|
||||
maximum: 1
|
||||
# MD013/line-length - Line length
|
||||
#
|
||||
MD013:
|
||||
# Number of characters
|
||||
line_length: 80
|
||||
# Number of characters for headings
|
||||
heading_line_length: 80
|
||||
# Number of characters for code blocks
|
||||
code_block_line_length: 160
|
||||
# Include code blocks
|
||||
code_blocks: false
|
||||
# Include tables
|
||||
tables: false
|
||||
# Include headings
|
||||
headings: true
|
||||
# Strict length checking (e.g. allow for longer URLs)
|
||||
strict: false
|
||||
# Stern length checking
|
||||
stern: false
|
||||
|
||||
# MD014/commands-show-output - Dollar signs used before commands without showing output
|
||||
# TODO: set false for now but we should consider enabling it
|
||||
# https://cirosantilli.com/markdown-style-guide#dollar-signs-in-shell-code
|
||||
MD014: false
|
||||
|
||||
# MD018/no-missing-space-atx - No space after hash on atx style heading
|
||||
MD018: true
|
||||
|
||||
# MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading
|
||||
MD019: true
|
||||
|
||||
# MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines
|
||||
MD022:
|
||||
# Blank lines above heading
|
||||
lines_above: 1
|
||||
# Blank lines below heading
|
||||
lines_below: 1
|
||||
|
||||
# MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line
|
||||
MD023: true
|
||||
|
||||
# MD025/single-title/single-h1 - Multiple top-level headings in the same document
|
||||
# TODO: consider enabling it
|
||||
|
||||
# MD026/no-trailing-punctuation - Trailing punctuation in heading
|
||||
MD026:
|
||||
# Punctuation characters
|
||||
punctuation: ".,;:!。,;:!"
|
||||
|
||||
# MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol
|
||||
MD027: true
|
||||
|
||||
# MD028/no-blanks-blockquote - Blank line inside blockquote
|
||||
MD028: true
|
||||
|
||||
# MD029/ol-prefix - Ordered list item prefix
|
||||
MD029:
|
||||
# List style
|
||||
style: "one_or_ordered"
|
||||
|
||||
# MD030/list-marker-space - Spaces after list markers
|
||||
MD030:
|
||||
# Spaces for single-line unordered list items
|
||||
ul_single: 1
|
||||
# Spaces for single-line ordered list items
|
||||
ol_single: 1
|
||||
# Spaces for multi-line unordered list items
|
||||
ul_multi: 1
|
||||
# Spaces for multi-line ordered list items
|
||||
ol_multi: 1
|
||||
|
||||
# MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines
|
||||
MD031:
|
||||
# Include list items
|
||||
list_items: true
|
||||
|
||||
# MD033/no-inline-html - Inline HTML
|
||||
# MD033:before uncomment, update docs/dasharo-tools-suite/documentation.md:18:1
|
||||
# and docs/variants/msi_z690/initial-deployment.md:14:1
|
||||
# Allowed elements
|
||||
# allowed_elements: ["br", "center", "img", "script", "form", "input", "iframe"]
|
||||
|
||||
# MD034/no-bare-urls - Bare URL used
|
||||
MD034: false
|
||||
|
||||
# MD035/hr-style - Horizontal rule style
|
||||
MD035:
|
||||
# Horizontal rule style
|
||||
style: "---"
|
||||
|
||||
# MD037/no-space-in-emphasis - Spaces inside emphasis markers
|
||||
MD037: true
|
||||
|
||||
# MD038/no-space-in-code - Spaces inside code span elements
|
||||
# MD038: true - Rule temporarily disabled, it prevents new tabs from being
|
||||
# formatted correctly - eg. docs/unified/novacustom/building-manual.md:46
|
||||
|
||||
# MD039/no-space-in-links - Spaces inside link text
|
||||
MD039: true
|
||||
|
||||
# MD040/fenced-code-language - Fenced code blocks should have a language specified
|
||||
MD040: true
|
||||
|
||||
# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading
|
||||
MD041:
|
||||
# Heading level
|
||||
level: 1
|
||||
# RegExp for matching title in front matter
|
||||
front_matter_title: "^\\s*title\\s*[:=]"
|
||||
|
||||
# MD042/no-empty-links - No empty links
|
||||
MD042: true
|
||||
|
||||
# MD046/code-block-style - Code block style
|
||||
# MD046: Rule temporarily disabled, it prevents new tabs from being formatted
|
||||
# correctly - eg. docs/unified/novacustom/overview.md:9
|
||||
# Block style
|
||||
# style: "fenced"
|
||||
|
||||
# MD047/single-trailing-newline - Files should end with a single newline character
|
||||
MD047: true
|
||||
|
||||
# MD048/code-fence-style - Code fence style
|
||||
MD048:
|
||||
# Code fence style
|
||||
style: "backtick"
|
||||
|
||||
# MD049/emphasis-style - Emphasis style should be consistent
|
||||
MD049:
|
||||
# Emphasis style should be consistent
|
||||
style: "underscore"
|
||||
|
||||
# MD050/strong-style - Strong style should be consistent
|
||||
MD050:
|
||||
# Strong style should be consistent
|
||||
style: "asterisk"
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
default_stages: [pre-commit]
|
||||
|
||||
default_install_hook_types: [pre-commit, commit-msg]
|
||||
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-merge-conflict
|
||||
- id: check-symlinks
|
||||
- id: detect-private-key
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- id: mixed-line-ending
|
||||
args: [--fix=lf]
|
||||
|
||||
- repo: https://github.com/igorshubovych/markdownlint-cli
|
||||
rev: v0.41.0
|
||||
hooks:
|
||||
- id: markdownlint
|
||||
- id: markdownlint-fix
|
||||
@@ -0,0 +1,51 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Add ways to verify the files
|
||||
|
||||
### Changed
|
||||
|
||||
- Refactored GitHub Actions workflow **Build PDF** (`scripts\build_guide_pdf.py`): now builds both light and dark mode PDFs (`export/thgtoa.pdf` and `export/thgtoa-dark.pdf` respectively).
|
||||
- Restored previous VT scans workflow **VirusTotal Scan** (`.github/workflows/vt-scan.yml`): submit files to VT for malware scanning. Links will be published on the site.
|
||||
|
||||
## Fixed
|
||||
|
||||
- `docs/about/index.md`: replace broken reference-style internal links
|
||||
- `docs/guide/index.md`: Appendix A6: comment out deprecated ODT information because we don't and probably won't use it in the future
|
||||
|
||||
### Feature
|
||||
|
||||
- Updated `scripts/build_guide_pdf.py` to use `--print-to-pdf` instead of `--save-as` for PDF generation, and added a new `--dark-mode` flag to generate dark mode PDFs. The script now supports generating both light and dark mode PDFs with a single command invocation by using the `--both` flag. This change improves the PDF generation process and provides better support for dark mode users. Save your eyes - you only get one pair.
|
||||
|
||||
## [1.2.1] - 2026-04-11
|
||||
|
||||
### Added
|
||||
|
||||
- GitHub Actions workflow **Build PDF** (`.github/workflows/build-pdf.yml`): installs Chromium on `ubuntu-latest`, runs `scripts/build_guide_pdf.py`, uploads `export/guide.pdf` as the `guide-pdf` artifact. Runs on `workflow_dispatch`, on pushes to `main` that touch docs or build inputs, and on matching pull requests.
|
||||
|
||||
- `scripts/build_guide_pdf.py` to build the MkDocs site and render the guide to a single PDF (`export/guide.pdf` by default) using a Chromium-based browser (Chrome or Edge) headless print-to-PDF.
|
||||
- `docs/stylesheets/extra.css` and `extra_css` in `mkdocs.yml` for shared site styling.
|
||||
- This `CHANGELOG.md`.
|
||||
|
||||
### Changed
|
||||
|
||||
- `README.md` “Ways to read or export the guide”: hosted link, local `mkdocs serve`, PDF build via the script, ODT note, raw Markdown link.
|
||||
- Guide landing layout: wrap the opening block in `docs/guide/index.md` with a `guide-intro-lead` container so the logo and first sections share one layout context for web and print.
|
||||
- `.gitignore` to exclude local build outputs `export/`, `site/`, and `_site_test/`.
|
||||
- `scripts/build_guide_pdf.py`: when the `CI` environment variable is set, pass Chromium flags (`--no-sandbox`, `--disable-setuid-sandbox`, `--disable-dev-shm-usage`) so headless print works on typical CI images.
|
||||
- `README.md`: note the **Build PDF** GitHub Actions workflow and the `guide-pdf` artifact.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `docs/guide/index.md`: replace broken reference-style internal links (`[label][label:]`) with working same-page fragment links to the correct headings; correct the mismatched “Real-Name System” cross-reference; fix a broken footnote marker on the “free (unallocated) space of your hard drive” list item.
|
||||
|
||||
[Unreleased]: https://github.com/Anon-Planet/thgtoa/compare/v1.2.1...HEAD
|
||||
[1.2.1]: https://github.com/Anon-Planet/thgtoa/releases/tag/v1.2.1
|
||||
@@ -0,0 +1,14 @@
|
||||
Welcome.
|
||||
|
||||
**[IMPORTANT RECOMMENDATION FOR UKRAINIANS. ВАЖЛИВА РЕКОМЕНДАЦІЯ ДЛЯ УКРАЇНЦІВ](briar.html)**
|
||||
|
||||
This is a maintained guide with the aim of providing an introduction to various online tracking techniques, online ID verification techniques, and detailed guidance to creating and maintaining (truly) anonymous online identities. <span style="color: red">**It is written with hope for activists, journalists, scientists, lawyers, whistle-blowers, and good people being oppressed, censored, harassed anywhere!**</span> This guide has no affiliation with the [Anonymous](https://en.wikipedia.org/wiki/Anonymous_(hacker_group)) <sup>[[Wikiless]](https://wikiless.com/wiki/Anonymous_(hacker_group))</sup> <sup>[[Archive.org]](https://web.archive.org/web/https://en.wikipedia.org/wiki/Anonymous_(hacker_group))</sup> collective/movement.
|
||||
|
||||
This guide is an open-source non-profit initiative, [licensed](LICENSE.html) under **Creative Commons Attribution-NonCommercial 4.0 International** ([cc-by-nc-4.0](https://creativecommons.org/licenses/by-nc/4.0/) <sup>[[Archive.org]](https://web.archive.org/web/https://creativecommons.org/licenses/by-nc/4.0/)</sup>) and is **not sponsored/endorsed by any commercial/governmental entity**. This means that you are free to use our guide for pretty much any purpose **excluding commercially** as long as you do attribute it. There are no ads or any affiliate links.
|
||||
|
||||
**If you would like to make a donation to help this project, you can do so from [here](donations.html) where you will also find the project goals. All the donations will be strictly used within the context of this project. All donations and spendings are logged on the donations page.**
|
||||
|
||||
**Ways to read or export the guide**
|
||||
|
||||
- **In your browser:** [Hitchhiker's Guide](https://www.anonymousplanet.org/guide/) (hosted site). After a local build you can also open `site/guide/index.html` directly.
|
||||
- **Local HTML preview:** from the repository root, with Python 3 and [MkDocs Material](https://squidfunk.github.io/mkdocs-material/getting-started/) installed (`pip install mkdocs-material`), run `mkdocs serve` and open the URL printed in the terminal (for example `http://127.0.0.1:8000`).
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm upload/*.minisig &> /dev/null
|
||||
rm upload/*.asc &> /dev/null
|
||||
rm upload/*.odt &> /dev/null
|
||||
rm upload/*.pdf &> /dev/null
|
||||
rm upload/*.txt &> /dev/null
|
||||
rm upload/sha256sum.txt &> /dev/null
|
||||
rm upload/b2sum.txt &> /dev/null
|
||||
rm -r upload/{.,}* &> /dev/null
|
||||
|
||||
true
|
||||
@@ -0,0 +1 @@
|
||||
anonymousplanet.org
|
||||
+17
-16
@@ -15,13 +15,13 @@ schema:
|
||||
---
|
||||
{ align=right }
|
||||
|
||||
**Anonymous Planet** are the maintainers of the [*Hitchhiker's Guide*](https://anonymousplanet.org/guide.html) and the [*PSA Community*](https://psa.anonymousplanet.org). It is responsible for maintaining the projects and code repositories.
|
||||
**Anonymous Planet** are the maintainers of the [_Hitchhiker's Guide_](../guide/index.md) and the [_PSA Community_](https://psa.anonymousplanet.org). It is responsible for maintaining the projects and code repositories. This project is part of our ongoing efforts to provide open-source tools and resources for the community, with regular updates and improvements added to the changelog.
|
||||
|
||||
The purpose: providing an introduction to various online tracking techniques, online ID verification techniques, and detailed guidance to creating and maintaining (truly) anonymous online identities. It is written in the hopes that activists, journalists, scientists, lawyers, whistle-blowers, etc. - i.e., good people - will be able to fight oppression, censorship and harassment! The website and projects are free (as in freedom) and not affiliated with any donor or projects discussed.
|
||||
The purpose: providing an introduction to various online tracking techniques, online ID verification techniques, and detailed guidance to creating and maintaining (truly) anonymous online identities. It is written with the hopes that good people (e.g., activists, journalists, scientists, lawyers, whistle-blowers, etc.) will be able to fight oppression, censorship and harassment! The website and projects are free (as in freedom) and not affiliated with any donor or projects discussed.
|
||||
|
||||
??? Note "Where do I start?"
|
||||
|
||||
Start either by going to [the beginning](/guide/) or using the search at top right of the page. It is also available at whatever point you are in your reading.
|
||||
Start either by going to [the beginning](../guide/index.md) or using the search at top right of the page. It is also available at whatever point you are in your reading.
|
||||
|
||||
??? Note "Notes on the journey"
|
||||
|
||||
@@ -33,9 +33,9 @@ The purpose: providing an introduction to various online tracking techniques, on
|
||||
|
||||
This guide is a non-profit open-source initiative, licensed under Creative Commons **Attribution-NonCommercial** 4.0 International ([cc-by-nc-4.0](https://creativecommons.org/licenses/by-nc/4.0/) <sup>[[Archive.org]](https://web.archive.org/web/https://creativecommons.org/licenses/by-nc/4.0/)</sup>).
|
||||
|
||||
- For mirrors see [Mirrors](../mirrors) and the links at the bottom right of the page. You should see these on every page.
|
||||
- For mirrors see [Mirrors](../mirrors/index.md) and the links at the bottom right of the page. You should see these on every page.
|
||||
|
||||
- For help in comparing versions see [Comparing versions](/guide/#appendix-a7-comparing-versions)
|
||||
- For help in comparing versions see [Comparing versions](../guide/index.md#appendix-a6-comparing-versions)
|
||||
|
||||
Feel free to submit issues **(please do report anything wrong)** using GitHub Issues at: <https://github.com/Anon-Planet/thgtoa/issues>. We also accept Merge Requests (MR) from our Gitlab and many other places. Do not hesitate to report issues and suggestions!
|
||||
|
||||
@@ -43,9 +43,10 @@ Feel free to submit issues **(please do report anything wrong)** using GitHub Is
|
||||
|
||||
We offer a Matrix.org hosted space of our own. Check it out!
|
||||
|
||||
- Read [the rules](https://anonymousplanet.org/chatrooms-rules.html), please
|
||||
- Matrix Room: [#anonymity:matrix.org](https://matrix.to/#/#anonymity:matrix.org)
|
||||
- Matrix Space: [#privacy-security-anonymity:matrix.org](https://matrix.to/#/#privacy-security-anonymity:matrix.org)
|
||||
- Read [the rules](https://psa.anonymousplanet.org/), please
|
||||
- Matrix Room: https://matrix.to/#/#nth:anonymousplanet.net
|
||||
- Matrix Space: https://matrix.to/#/#psa:anonymousplanet.net
|
||||
- Admins: @daskolburn:thomcat.rocks and @thehidden:tchncs.de
|
||||
|
||||
Follow us on:
|
||||
|
||||
@@ -55,9 +56,10 @@ Follow us on:
|
||||
|
||||
To contact me, see the updated information on the website or send an e-mail to <contact@anonymousplanet.org>
|
||||
|
||||
**Please consider [donating](/donate/) if you enjoy the project and want to support the hosting fees or support the funding of initiatives like the hosting of Tor Exit Nodes.**
|
||||
**Please consider [donating](../guide/index.md#donations) if you enjoy the project and want to support the hosting fees or support the funding of initiatives like the hosting of Tor Exit Nodes.**
|
||||
|
||||
### Recommended Reading
|
||||
|
||||
Some of those resources may, in order to sustain their project, contain or propose:
|
||||
|
||||
- Sponsored commercial content
|
||||
@@ -67,12 +69,11 @@ Some of those resources may, in order to sustain their project, contain or propo
|
||||
- Premium content such as ad-free content or updated content
|
||||
- Merchandising
|
||||
|
||||
*Note that these websites could contain affiliate/sponsored content and/or merchandising. This guide does not endorse and is not sponsored by any commercial entity in any way.*
|
||||
_Note that these websites could contain affiliate/sponsored content and/or merchandising. This guide does not endorse and is not sponsored by any commercial entity in any way._
|
||||
|
||||
If you skipped those, you should really still consider viewing this YouTube playlist from the Techlore Go Incognito project (<https://github.com/techlore-official/go-incognito> <sup>[[Archive.org]](https://web.archive.org/web/https://github.com/techlore-official/go-incognito)</sup>) as an introduction before going further: <https://www.youtube.com/playlist?list=PL3KeV6Ui_4CayDGHw64OFXEPHgXLkrtJO> <sup>[[Invidious]](https://yewtu.be/playlist?list=PL3KeV6Ui_4CayDGHw64OFXEPHgXLkrtJO)</sup>. This guide will cover many of the topics in the videos of this playlist with more details and references as well as some added topics not covered within that series. This will just take you 2 or 3 hours to watch it all.
|
||||
|
||||
|
||||
*Anonymous Planet* **does not** participate in any sponsoring, endorsement, advertising, or other affiliate programs for any entity. We only rely on anonymous donations in a closed, transparent loop system.
|
||||
_Anonymous Planet_ **does not** participate in any sponsoring, endorsement, advertising, or other affiliate programs for any entity. We only rely on anonymous donations in a closed, transparent loop system.
|
||||
|
||||
??? Note "Privacy related"
|
||||
|
||||
@@ -94,17 +95,17 @@ If you skipped those, you should really still consider viewing this YouTube play
|
||||
??? Note "Useful resources"
|
||||
|
||||
- KYC? Not me: <https://kycnot.me/>
|
||||
- Library Genesis: <https://en.wikipedia.org/wiki/Library_Genesis> <sup>[[Wikiless]](https://wikiless.org/wiki/Library_Genesis)</sup> (see their latest known URL in the Wikipedia article)
|
||||
- Library Genesis: <https://en.wikipedia.org/wiki/Library_Genesis> <sup>[[Wikiless]](https://wikiless.com/wiki/Library_Genesis)</sup> (see their latest known URL in the Wikipedia article)
|
||||
- Real World Onion Sites: <https://github.com/alecmuffett/real-world-onion-sites>
|
||||
- Sci-Hub <https://en.wikipedia.org/wiki/Sci-Hub> <sup>[[Wikiless]](https://wikiless.org/wiki/Sci-Hub)</sup> (see their latest known URL in the main Wikipedia article)
|
||||
- Sci-Hub <https://en.wikipedia.org/wiki/Sci-Hub> <sup>[[Wikiless]](https://wikiless.com/wiki/Sci-Hub)</sup> (see their latest known URL in the main Wikipedia article)
|
||||
- Terms of Service, Didn't Read: <https://tosdr.org>
|
||||
- Whonix Documentation: <https://www.whonix.org/wiki/Documentation>
|
||||
|
||||
!!! Note "We are not affiliated with Anonymous or Riseup"
|
||||
??? Note "We are not affiliated with Anonymous or Riseup"
|
||||
|
||||
One or two of our community members uses or has used the resources of Riseup. We are not affiliated with Riseup in any manner.
|
||||
|
||||
We also hold **no affiliation** with the [Anonymous](https://en.wikipedia.org/wiki/Anonymous_(hacker_group)) <sup>[[Wikiless]](https://wikiless.org/wiki/Anonymous_(hacker_group))</sup> <sup>[[Archive.org]](https://web.archive.org/web/https://en.wikipedia.org/wiki/Anonymous_(hacker_group))</sup> hacker collective.
|
||||
We also hold **no affiliation** with the [Anonymous](https://en.wikipedia.org/wiki/Anonymous_(hacker_group)) <sup>[[Wikiless]](https://wikiless.com/wiki/Anonymous_(hacker_group))</sup> <sup>[[Archive.org]](https://web.archive.org/web/https://en.wikipedia.org/wiki/Anonymous_(hacker_group))</sup> hacker collective.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
title: Content Contributions
|
||||
---
|
||||
You can [submit bugs and feature requests](https://github.com/Anon-Planet/thgtoa/issues/new) with detailed information about your issue or idea:
|
||||
|
||||
- If you'd like to propose an addition, please follow the standards outlined here.
|
||||
- If you're reporting an issue, please be sure to include the expected behaviour, the observed behaviour, and steps to reproduce the problem.
|
||||
- This can require technical knowledge, but you can also get involved in conversations about bug reports and feature requests. This is a great way to get involved without getting too overwhelmed!
|
||||
- [Help fellow committers test recently submitted pull requests](https://github.com/Anon-Planet/thgtoa/pulls). Simply by pulling down a pull request and testing it, you can help ensure our new code contributions for stability and quality.
|
||||
|
||||
For those of you who are looking to add content to the guide, include the following:
|
||||
|
||||
##### <u>Pull Requests</u>
|
||||
|
||||
- **Do** create a [topic branch] to work on instead of working directly on `main`. This helps to:
|
||||
+ Protect the process.
|
||||
+ Ensures users are aware of commits on the branch being considered for merge.
|
||||
+ Allows for a location for more commits to be offered without mingling with other contributor changes.
|
||||
+ Allows contributors to make progress while a PR is still being reviewed.
|
||||
- **Do** follow the [50/72 rule] for Git commit messages.
|
||||
- **Do** write "WIP" on your PR and/or open a [draft PR] if submitting unfinished changes..
|
||||
- **Do** make sure the title of a draft PR makes it immediately clear that it's a draft
|
||||
- **Do** target your pull request to the **main branch**.
|
||||
- **Do** specify a descriptive title to make searching for your pull request easier.
|
||||
- **Don't** leave your pull request description blank.
|
||||
- **Don't** abandon your pull request. Being responsive helps us land your changes faster.
|
||||
- **Don't** post questions in older closed PRs.
|
||||
- **Do** stick to the guide to find common style issues.
|
||||
- **Don't** make mass changes (such as replacing "I" with "we") using automated serach/replace functionality.
|
||||
+ Search/replace doesn't understand context, and as such, will inevitably cause inconsistencies and make the guide harder to read.
|
||||
+ If it's part of a larger PR, it'll also make the reviewer's life harder, as they'll have to go through manually and undo everything by hand.
|
||||
+ _If you're going to make mass changes, take the time to do it properly_. Otherwise we'll just have to undo it anyway.
|
||||
+ If your change contains backslashes (`\`), either escape them with another backslash (`\\`) or put them in a ```code block```.
|
||||
|
||||
When reporting guide issues:
|
||||
|
||||
- **Do** write a detailed description of your issue and use a descriptive title.
|
||||
- **Do** make it as detailed as possible and don't just submit 50 line changes without explaining.
|
||||
- **Don't** file duplicate reports; search for your bug before filing a new report.
|
||||
- **Don't** attempt to report issues on a closed PR.
|
||||
|
||||
### Large PRs
|
||||
|
||||
Please split large sets of changes into multiple PRs. For example, a PR that adds Windows 11 support, removes Windows AME references, and fixes typos can be split into 3 PRs. This makes PRs easier to review prior to merging.
|
||||
|
||||
For an example of what _not_ to do, see: <https://github.com/Anon-Planet/thgtoa/pull/51>. This PR contains enough changes to split into multiple smaller and individually reviewable PRs.
|
||||
|
||||
### Updating PRs
|
||||
|
||||
While a PR is being reviewed, modifications may be made to it by the reviewer prior to merging. If this is the case, a new branch will be created for the PR's review. If you would like to submit a change to a PR that is in the process of being reviewed, _do not update the PR directly_. This will only cause merge conflicts and delay the PR from being merged. Instead, submit your changes to the PR's review branch.
|
||||
|
||||
For an example of what _not_ to do, see: <https://github.com/Anon-Planet/thgtoa/pull/51>. Instead of submitting changes to the PR directly, they should have been submitted as changes to the PR's associated review branch.
|
||||
|
||||
---
|
||||
|
||||
**Thank you** for taking the few moments to read this far! You're already way ahead of the
|
||||
curve, so keep it up!
|
||||
|
||||
[discussions]: https://github.com/Anon-Planet/thgtoa/discussions
|
||||
[issues]: https://github.com/Anon-Planet/thgtoa/issues
|
||||
[help fellow users with open issues]: https://github.com/Anon-Planet/thgtoa/issues
|
||||
[topic branch]: http://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches
|
||||
[Qubes#7457]: https://github.com/QubesOS/qubes-issues/issues/7457
|
||||
[50/72 rule]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
[draft pr]: https://help.github.com/en/articles/about-pull-requests#draft-pull-requests
|
||||
[console output]: https://docs.github.com/en/free-pro-team@latest/github/writing-on-github/creating-and-highlighting-code-blocks#fenced-code-blocks
|
||||
[verification steps]: https://docs.github.com/en/free-pro-team@latest/github/writing-on-github/basic-writing-and-formatting-syntax#task-lists
|
||||
[reference associated issues]: https://github.com/blog/1506-closing-issues-via-pull-requests
|
||||
[help fellow committers test recently submitted pull requests]: https://github.com/Anon-Planet/thgtoa/pulls
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: How to Get Involved
|
||||
---
|
||||
Donations to support this project are welcome. Those donations are mainly used to pay for Tor onion hosting (VPS), mail hosting, domain name registration, and to maintain/run Tor exit nodes. **No profit is ever being made**. All donations and spendings are being logged here below for transparency.
|
||||
There are multiple ways you can add to the guide. Donations to support this project are welcome but are entirely optional. Those donations are mainly used to pay for Tor onion hosting (VPS), mail hosting, domain name registration, and to maintain/run Tor exit nodes. **No profit is ever being made**. All donations and spendings are being logged here below for transparency. Some costs for load balancer servers have been omitted for privacy reasons, but are not paid for with existing Anonymous Planet finances.
|
||||
|
||||
<span style="color: red">**Current project donation goals:**</span>
|
||||
|
||||
@@ -10,7 +10,7 @@ Donations to support this project are welcome. Those donations are mainly used t
|
||||
- Funding for a decent mail hosting
|
||||
- Funding for a VPS for hosting various services
|
||||
|
||||
#### Donate using Monero (XMR)
|
||||
## Donate using Monero (XMR)
|
||||
|
||||
Total Monero donations received: **7.101317184263 XMR**
|
||||
Total Monero remaining: **2.059336719397 XMR**
|
||||
@@ -21,7 +21,7 @@ Here is the address for the main project:
|
||||
|
||||
![][1]
|
||||
|
||||
#### Donate using Bitcoin (BTC)
|
||||
## Donate using Bitcoin (BTC)
|
||||
|
||||
Total Bitcoin donations received: **1.89353 mBTC**
|
||||
Total Bitcoin remaining: **0 mBTC**
|
||||
@@ -33,10 +33,13 @@ Legacy address: ```1BBgBSVe6w4DWq2BewUQhDEjsNovhfPswD```
|
||||
|
||||
![][2]_____________________![][3]
|
||||
|
||||
## Content Contributions
|
||||
|
||||
You can easily contribute code or information suggestions at our code repositories listed at the bottom of the website and on the [Mirrors](../mirrors/index.md) tab above. We have many options that are easily accessible. Please follow our [contributing guidelines](../code/index.md) and use good PR syntax.
|
||||
|
||||
**Thank you for any contribution. All donations will be mentioned on this page.**
|
||||
|
||||
#### Donations log
|
||||
### Donations log
|
||||
|
||||
- 2021-02-06 16:48: 0.1 XMR
|
||||
- 2021-03-15 00:09: 1.24869 mBTC
|
||||
@@ -103,7 +106,6 @@ Legacy address: ```1BBgBSVe6w4DWq2BewUQhDEjsNovhfPswD```
|
||||
- <del>2022-07-11: 0.503232784687 XMR (+fees) for 1984.is VPS (12 months)</del>: <span style="color: red">**Ended**</span>
|
||||
- <del>2022-09-19: 0.345024603905 XMR (+fees) for upgrading VPS RAM/Disk</del>: <span style="color: red">**Ended**</span>
|
||||
|
||||
|
||||
[1]: ../media/monero.png
|
||||
[2]: ../media/bitcoin-segwit.png
|
||||
[3]: ../media/bitcoin-legacy.png
|
||||
@@ -0,0 +1,193 @@
|
||||
# Development
|
||||
|
||||
## Overview
|
||||
|
||||
This repository now includes an automated workflow that handles PDF generation, verification, and distribution with the following features:
|
||||
|
||||
??? Note "How the pipeline works"
|
||||
|
||||
1. **Automatic PDF Generation** - Builds both light and dark mode PDFs from MkDocs source
|
||||
2. **SHA256 Hash Generation** - Creates hash files for integrity verification
|
||||
3. **GPG Signature Signing** - Signs all PDFs and hash files with repository GPG key
|
||||
4. **VirusTotal Scanning** - Automatically scans PDFs and updates release notes
|
||||
5. **Release Automation** - Packages everything into GitHub releases
|
||||
|
||||
## Workflow Architecture
|
||||
|
||||
### 1. Build PDF Workflow (`build-pdf.yml`)
|
||||
|
||||
**Trigger:** Push to main, pull requests, or manual dispatch
|
||||
|
||||
??? Note "Steps"
|
||||
|
||||
- Checkout repository
|
||||
- Set up Python 3.13 and MkDocs Material
|
||||
- Install Chromium browser
|
||||
- Generate both light and dark mode PDFs
|
||||
- Create SHA256 hash files
|
||||
- Sign all files with GPG
|
||||
- Upload artifacts to GitHub Actions
|
||||
- Publish release
|
||||
|
||||
### 2. VirusTotal Scan Workflow (`vt-scan.yml`)
|
||||
|
||||
**Trigger:** Push to main, tags, or manual dispatch (runs after build-pdf)
|
||||
|
||||
??? Note "Steps"
|
||||
|
||||
- Download PDF artifacts from build workflow
|
||||
- Scan both PDFs with VirusTotal API
|
||||
- Extract scan results and generate report links
|
||||
- Update release notes with VT scan status and URLs
|
||||
|
||||
## File Structure
|
||||
|
||||
After a successful build, the repository will contain:
|
||||
|
||||
```
|
||||
.../
|
||||
├── export/
|
||||
│ ├── thgtoa.pdf # Light mode PDF
|
||||
│ ├── thgtoa-dark.pdf # Dark mode PDF
|
||||
│ ├── thgtoa.pdf.sig # GPG signature (light)
|
||||
│ └── thgtoa-dark.pdf.sig # GPG signature (dark)
|
||||
├── thgtoa.pdf.sha256 # Hash file (light)
|
||||
├── thgtoa-dark.pdf.sha256 # Hash file (dark)
|
||||
├── sha256sum-light.txt # Combined hash file
|
||||
└── scripts/
|
||||
├── build_guide_pdf.py # PDF generation script
|
||||
└── verify_pdf.py # Verification utility
|
||||
```
|
||||
|
||||
## Security Features
|
||||
|
||||
### 1. SHA256 Hash Verification
|
||||
|
||||
**Purpose:** Ensure file integrity during download/transit
|
||||
|
||||
**How it works:**
|
||||
- Each PDF gets a unique SHA256 hash calculated at build time
|
||||
- Hash stored in `.sha256` files alongside the PDFs
|
||||
- Combined `sha256sum-light.txt` for batch verification
|
||||
|
||||
**Verification command:**
|
||||
```bash
|
||||
sha256sum -c sha256sum-light.txt
|
||||
```
|
||||
|
||||
### 2. GPG Signature Verification
|
||||
|
||||
**Purpose:** Verify authenticity and prevent tampering
|
||||
|
||||
??? Note "How it works"
|
||||
|
||||
- Detached signatures created for each PDF and hash file
|
||||
- Public keys available in `/pgp/` directory
|
||||
|
||||
**Verification command:**
|
||||
```bash
|
||||
gpg --import pgp/anonymousplanet-master.asc
|
||||
gpg --verify export/thgtoa.pdf.sig export/thgtoa.pdf
|
||||
```
|
||||
|
||||
### 3. VirusTotal Integration
|
||||
|
||||
**Purpose:** Malware detection and security scanning
|
||||
|
||||
??? Note "How it works"
|
||||
|
||||
- Automatic scan of all generated PDFs
|
||||
- Results published in release notes with direct links
|
||||
- Provides third-party validation of file safety
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Local Development
|
||||
|
||||
```bash
|
||||
# Build PDFs locally
|
||||
python scripts/build_guide_pdf.py --both
|
||||
|
||||
# Verify hashes
|
||||
python scripts/verify_pdf.py --hashes
|
||||
|
||||
# Verify signatures (requires GPG installed)
|
||||
python scripts/verify_pdf.py --signatures
|
||||
|
||||
# Full verification with VirusTotal check
|
||||
export VT_API_KEY=your_api_key
|
||||
python scripts/verify_pdf.py --all
|
||||
```
|
||||
|
||||
### CI/CD Verification
|
||||
|
||||
The workflows automatically verify everything during the build process. To manually trigger:
|
||||
|
||||
1. Go to Actions tab
|
||||
2. Select "Build guide PDF" or "VirusTotal Scan"
|
||||
3. Click "Run workflow"
|
||||
4. Download artifacts from successful run
|
||||
|
||||
## Release Process
|
||||
|
||||
When you create a tag (e.g., `v1.0.0`):
|
||||
|
||||
1. Push the tag: `git push origin v1.0.0`
|
||||
2. Build PDF workflow triggers automatically
|
||||
3. VirusTotal scan workflow runs after build completes
|
||||
4. Both workflows update/create GitHub release with:
|
||||
- Light and dark mode PDFs
|
||||
- GPG signatures for all files
|
||||
- Hash files for verification
|
||||
- Release notes with VT scan results
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**GPG signing fails:**
|
||||
- Check that `GPG_PRIVATE_KEY` is in ASCII armor format
|
||||
- Verify passphrase is correct
|
||||
- Ensure key has signing capability
|
||||
|
||||
**Hash mismatch after download:**
|
||||
- Re-download the file (corruption during transfer)
|
||||
- Verify you're using the correct hash file
|
||||
- Check disk integrity
|
||||
|
||||
**VirusTotal scan fails:**
|
||||
- Verify `VT_API_KEY` is set correctly
|
||||
- Check API quota limits (free tier: 4 requests/minute)
|
||||
- Ensure PDF files exist before scanning
|
||||
|
||||
### Debug Mode
|
||||
|
||||
Enable verbose output by adding to workflow:
|
||||
```yaml
|
||||
- name: Debug
|
||||
run: |
|
||||
echo "Current directory:" && pwd
|
||||
echo "Files in export:" && ls -la export/
|
||||
echo "Hash file contents:" && cat sha256sum-light.txt
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Always verify signatures** before opening PDFs from untrusted sources
|
||||
2. **Check VirusTotal results** for any suspicious detections
|
||||
3. **Keep GPG keys secure** - never commit private keys to repository
|
||||
4. **Monitor API usage** for VirusTotal to avoid rate limiting
|
||||
5. **Test locally** before pushing tags to production
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements:
|
||||
- Multi-signature support (multiple maintainers)
|
||||
- Automated changelog generation with hashes
|
||||
- Cross-platform signature verification scripts
|
||||
- Integration with additional malware scanners
|
||||
- Automatic mirror updates with verified files
|
||||
|
||||
---
|
||||
|
||||
*This workflow is designed for security-conscious users who need to verify the authenticity and integrity of downloaded documents.*
|
||||
+905
-907
File diff suppressed because it is too large
Load Diff
+16
-16
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: "Anonymous Planet"
|
||||
title: ""
|
||||
description: We are the maintainers of the Hitchhiker's Guide and the PSA Matrix space.
|
||||
schema:
|
||||
"@context": https://schema.org
|
||||
@@ -13,29 +13,29 @@ schema:
|
||||
- https://opencollective.com/anonymousplanetorg
|
||||
- https://mastodon.social/@anonymousplanet
|
||||
---
|
||||
|
||||
# **Hello, and welcome to the Hitchhiker's Guide.**
|
||||
|
||||
**9FA5 436D 0EE3 6098 5157 3825 17EC A05F 768D EDF6**
|
||||
|
||||
This is the master signing key fingerprint for Anonymous Planet.
|
||||
You'll use it to [**verify the checksum** and **GPG signature** of all files for authenticity.](verify/index.md)
|
||||
Please share this project if you enjoy it and you think it might be useful to others.
|
||||
|
||||
{ align=right }
|
||||
|
||||
**Welcome to Anonymous Planet's Hitchhiker's Guide.**
|
||||
|
||||
Please share this project if you enjoy it and you think it might be useful to others.
|
||||
|
||||
Anonymous Planet is a collective of volunteers and contributors. No one person is considered more valuable than another, and no one person should be viewed as having "more impact" on Anonymous Planet.
|
||||
|
||||
??? person "Anonymous Planet"
|
||||
|
||||
- [:simple-matrix: Our Matrix Space](https://matrix.to/#/#privacy-security-anonymity:matrix.org)
|
||||
|
||||
??? person "@alex"
|
||||
??? person "Das Kolburn"
|
||||
|
||||
- [:simple-github: GitHub](https://github.com/NobodySpecial256 "@NobodySpecial256")
|
||||
- [:fontawesome-solid-envelope: E-mail](mailto:theheadlessserpentsec@protonmail.com)
|
||||
- [:simple-matrix: Matrix](https://matrix.to/#/@memorysafetybelike:envs.net)
|
||||
- [:fontawesome-solid-envelope: E-mail](mailto:contact@anonymousplanet.org)
|
||||
- [:simple-matrix: Personal Matrix](https://matrix.to/#/@daskolburn:thomcat.rocks "@daskolburn:thomcat.rocks"), [:simple-matrix: Org Matrix](https://matrix.to/#/@daskolburn:anonymousplanet.net "@daskolburn:anonymousplanet.net")
|
||||
|
||||
??? person "@than"
|
||||
??? person "Nope"
|
||||
|
||||
- [:simple-github: GitHub](https://github.com/nopeitsnothing "@nopeitsnothing")
|
||||
- [:simple-mastodon: Mastodon](https://ioc.exchange/@unknown "@unknown@ioc.exchange"){rel=me}
|
||||
- [:fontawesome-solid-house: Homepage](https://www.itsnothing.net)
|
||||
- [:fontawesome-solid-envelope: E-mail](mailto:nopenothinghere@proton.me)
|
||||
- [:simple-matrix: Matrix](https://matrix.to/#/@thehidden:tchncs.de)
|
||||
- [:fontawesome-solid-shield: Canary](https://itsnothing.net/canary.txt)
|
||||
- [:fontawesome-solid-envelope: E-mail](mailto:contact@anonymousplanet.org)
|
||||
- [:simple-matrix: Personal Matrix](https://matrix.to/#/@thehidden:tchncs.de "@thehidden:tchncs.de"), [:simple-matrix: Org Matrix](https://matrix.to/#/@nope:anonymousplanet.net "@nope:anonymousplanet.net")
|
||||
|
||||
+10
-5
@@ -20,17 +20,22 @@ schema:
|
||||
!!! Note "Where to find the Hitchhiker's Guide"
|
||||
|
||||
- [Original](https://anonymousplanet.org)
|
||||
- (offline) [Tor Onion Mirror](http://thgtoa27ujspeqxasrfvcf5aozqdczvgmwgorrmblh6jn4nino3spcqd.onion)
|
||||
- [Tor v3](http://thgtoa3jzy3doku7hkna32htpghjijefscwvh4dyjgfydbbjkeiohgid.onion) **Down**
|
||||
- [Archive.org](https://web.archive.org/web/https://anonymousplanet.org)
|
||||
- [Archive.today](https://archive.fo/anonymousplanet.org)
|
||||
- [Archive.today over Tor](http://archiveiya74codqgiixo33q62qlrqtkgmcitqx5u2oeqnmn5bpcbiyd.onion/anonymousplanet.org)
|
||||
- [PDF](https://anonymousplanet.org/export/guide.pdf) <sup>[[Archive.org]](https://web.archive.org/web/https://anonymousplanet.org/export/guide.pdf)</sup> <sup>[[Tor Mirror]](http://thgtoa27ujspeqxasrfvcf5aozqdczvgmwgorrmblh6jn4nino3spcqd.onion/export/guide.pdf)</sup>
|
||||
- [ODT](https://anonymousplanet.org/export/guide.odt) <sup>[[Archive.org]](https://web.archive.org/web/https://anonymousplanet.org/export/guide.odt)</sup> <sup>[[Tor Mirror]](http://thgtoa27ujspeqxasrfvcf5aozqdczvgmwgorrmblh6jn4nino3spcqd.onion/export/guide.odt)</sup>
|
||||
|
||||
!!! Note "PDF export (single file)"
|
||||
|
||||
The guide is also available as a **PDF** (images and layout preserved). It is built automatically in GitHub Actions: open [**Build guide PDF**](https://github.com/Anon-Planet/thgtoa/actions/workflows/build-pdf.yml) on the [**source repository**](https://github.com/Anon-Planet/thgtoa), pick a successful run, and download the **`thgtoa`** and **`thgtoa-dark`** artifacts. You can start a fresh build anytime (**Actions** → **Build guide PDF** → **Run workflow**).
|
||||
|
||||
To produce the same file locally, clone the repository and run `python3 scripts/build_guide_pdf.py --both` (Python, [MkDocs Material](https://squidfunk.github.io/mkdocs-material/getting-started/), and **Google Chrome** or **Microsoft Edge** required). More detail is in the [repository README](https://github.com/Anon-Planet/thgtoa#ways-to-read-or-export-the-guide).
|
||||
|
||||
!!! Note "Our official git mirrors"
|
||||
|
||||
- [Github](https://github.com/anon-planet)
|
||||
- [Darktea](http://it7otdanqu7ktntxzm427cba6i53w6wlanlh23v5i3siqmos47pzhvyd.onion/anonymousplanetorg) (Tor)
|
||||
- [Darktea](http://it7otdanqu7ktntxzm427cba6i53w6wlanlh23v5i3siqmos47pzhvyd.onion/anonymousplanetorg) (Tor Only)
|
||||
- [Gitlab](https://gitlab.com/anonymousplanetorg)
|
||||
- [0xacab](http://wmj5kiic7b6kjplpbvwadnht2nh2qnkbnqtcv3dyvpqtz7ssbssftxid.onion/anonypla) (Tor)
|
||||
- [0xacab](https://0xacab.org/anonypla) ([Tor Onion](http://wmj5kiic7b6kjplpbvwadnht2nh2qnkbnqtcv3dyvpqtz7ssbssftxid.onion/anonypla))
|
||||
- [Codeberg](https://codeberg.org/anonymousplanet)
|
||||
- [Disroot](https://git.disroot.org/anon-planet) ([Tor Onion](http://kgtz2pmmov5jfvn3z4mqryffjnnw6krzrgxxoyaqhqckjrr4pckyhsqd.onion/anon-planet))
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/* Generate dark mode PDF of the HTML at guide/index.html */
|
||||
|
||||
/*
|
||||
DARK_MODE_PDF.CSS
|
||||
Use this stylesheet when generating a PDF from HTML.
|
||||
*/
|
||||
|
||||
:root {
|
||||
/* Color Palette */
|
||||
--bg-color: #121212; /* Deep dark grey (easier on eyes than pure black) */
|
||||
--text-primary: #e0e0e0; /* Off-white for readability */
|
||||
--text-secondary: #a0a0a0; /* Grey for captions/metadata */
|
||||
--accent-color: #bb86fc; /* Light purple accent (optional) */
|
||||
--border-color: #333333; /* Subtle borders */
|
||||
|
||||
/* Fonts - System fonts ensure best rendering across PDF engines */
|
||||
--font-main: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* --- RESET & BASE STYLES --- */
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-main);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* --- TYPOGRAPHY & HEADINGS --- */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
color: var(--text-primary);
|
||||
font-weight: 700;
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1rem;
|
||||
color: var(--text-secondary); /* Slightly dimmer text for body copy */
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* --- CONTAINER & LAYOUT --- */
|
||||
.container {
|
||||
max-width: 800px;
|
||||
margin: 40px auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Cards / Sections with dark backgrounds */
|
||||
.card {
|
||||
background-color: #1e1e1e;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* --- TABLES (Common in PDFs) --- */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #2c2c2c;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* --- IMAGES --- */
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 20px 0;
|
||||
/* This ensures high contrast images don't get washed out */
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
|
||||
/* --- CRITICAL FOR PDF GENERATORS --- */
|
||||
/* Forces the browser/PDF engine to print background colors and graphics */
|
||||
@media print {
|
||||
@page {
|
||||
size: A4; /* Change to 'Letter' if preferred */
|
||||
margin: 20mm;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-color) !important;
|
||||
color: var(--text-primary) !important;
|
||||
}
|
||||
|
||||
.card, table th {
|
||||
-webkit-print-color-adjust: exact !important; /* Chrome/Safari */
|
||||
print-color-adjust: exact !important; /* Firefox/Standard */
|
||||
background-color: #1e1e1e !important;
|
||||
color: var(--text-primary) !important;
|
||||
}
|
||||
|
||||
/* Prevent page breaks in the middle of a sentence or card if possible */
|
||||
.card {
|
||||
break-inside: avoid;
|
||||
}
|
||||
|
||||
/* Hide elements you don't want in PDF (like navigation bars) */
|
||||
nav, footer, button {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/* Title sheet: visible only when printing / generating PDF (not on screen). */
|
||||
.pdf-title-page {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.pdf-title-page {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
padding: 5rem 2rem 4rem;
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
.pdf-title-page__title {
|
||||
font-size: 1.65rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.25;
|
||||
margin: 0 0 1.25rem;
|
||||
}
|
||||
|
||||
.pdf-title-page__subtitle {
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.4;
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
|
||||
.pdf-title-page__meta {
|
||||
font-size: 0.95rem;
|
||||
font-style: normal;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Guide landing: small floating logo so opening copy flows beside it (HTML + PDF). */
|
||||
.guide-intro-lead {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.guide-intro-lead > p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.guide-intro-lead > p:first-child img {
|
||||
float: right;
|
||||
max-width: 6.5rem;
|
||||
height: auto;
|
||||
margin: 0 0 0.5rem 1rem;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.guide-intro-lead > p:first-child img {
|
||||
max-width: 5rem;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
---
|
||||
title: "Verify"
|
||||
description: How to verify the authenticity of our files and check virus scans
|
||||
---
|
||||
|
||||
# PDF Verification Guide
|
||||
|
||||
## Files Provided
|
||||
|
||||
For each PDF release, you'll receive:
|
||||
|
||||
- **PDF file** (`thgtoa.pdf` or `thgtoa-dark.pdf`) - The actual document
|
||||
- **Signature file** (`.sig`) - GPG detached signature for authenticity verification
|
||||
- **Hash file** (`.sha256`) - SHA256 checksum for integrity verification
|
||||
|
||||
## Quick Verification
|
||||
|
||||
### Using Python Script (Recommended)
|
||||
|
||||
```bash
|
||||
# Verify everything (hashes, signatures, and optionally VirusTotal)
|
||||
python scripts/verify_pdf.py --all
|
||||
|
||||
# Only verify hashes
|
||||
python scripts/verify_pdf.py --hashes
|
||||
|
||||
# Only verify GPG signatures
|
||||
python scripts/verify_pdf.py --signatures
|
||||
|
||||
# Check VirusTotal scan status (requires VT_API_KEY environment variable)
|
||||
python scripts/verify_pdf.py --vt
|
||||
```
|
||||
|
||||
### Manual Verification
|
||||
|
||||
#### 1. Verify SHA256 Hash
|
||||
|
||||
**Linux/macOS:**
|
||||
```bash
|
||||
cd /path/to/repo
|
||||
sha256sum -c sha256sum-light.txt
|
||||
```
|
||||
|
||||
**Windows (PowerShell):**
|
||||
```powershell
|
||||
Get-FileHash -Algorithm SHA256 export\thgtoa.pdf | Select-Object Hash
|
||||
# Compare with the hash in thgtoa.pdf.sha256
|
||||
```
|
||||
|
||||
#### 2. Verify GPG Signature
|
||||
|
||||
First, import the public key:
|
||||
```bash
|
||||
gpg --import pgp/anonymousplanet-master.asc
|
||||
```
|
||||
|
||||
Then verify the signature:
|
||||
```bash
|
||||
gpg --verify export/thgtoa.pdf.sig export/thgtoa.pdf
|
||||
gpg --verify export/thgtoa-dark.pdf.sig export/thgtoa-dark.pdf
|
||||
```
|
||||
|
||||
Expected output for successful verification:
|
||||
```
|
||||
gpg: Signature made [date]
|
||||
gpg: using RSA key [key-id]
|
||||
gpg: Good signature from "[owner]"
|
||||
```
|
||||
|
||||
#### 3. Check VirusTotal Status
|
||||
|
||||
Visit the VirusTotal report links (automatically generated in release notes):
|
||||
- Light mode: `https://www.virustotal.com/gui/file/[hash]`
|
||||
- Dark mode: `https://www.virustotal.com/gui/file/[hash]`
|
||||
|
||||
Or use the Python script with API key:
|
||||
```bash
|
||||
export VT_API_KEY=your_vt_api_key
|
||||
python scripts/verify_pdf.py --vt
|
||||
```
|
||||
|
||||
## Automated Verification in CI/CD
|
||||
|
||||
The GitHub Actions workflows automatically:
|
||||
|
||||
1. **Build PDFs** from MkDocs source
|
||||
2. **Generate SHA256 hashes** and save to root directory
|
||||
3. **Sign files with GPG** using the repository's private key
|
||||
4. **Scan with VirusTotal** and update release notes
|
||||
5. **Create releases** with all verification artifacts
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Always verify signatures** before opening PDFs from untrusted sources
|
||||
2. **Check hashes** to ensure files weren't corrupted during download
|
||||
3. **Review VirusTotal results** for any suspicious detections
|
||||
4. **Import keys securely** - verify key fingerprints with the project maintainers
|
||||
5. **Keep verification scripts updated** to match current security standards
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Good signature" but wrong owner?
|
||||
- Ensure you imported the correct public key
|
||||
- Check the key fingerprint matches the official one from the repository
|
||||
|
||||
### Hash mismatch?
|
||||
- Re-download the file (corruption during transfer)
|
||||
- Verify you're checking against the correct hash file
|
||||
- Check for disk errors on your system
|
||||
|
||||
### GPG not found?
|
||||
- Install GPG: `sudo apt install gnupg` (Debian/Ubuntu) or `brew install gnupg` (macOS)
|
||||
- On Windows, use [Gpg4win](https://www.gpg4win.org/)
|
||||
|
||||
## Key Information
|
||||
|
||||
**Signing Key:** Anonymous Planet Master Key
|
||||
**Key ID:** See `pgp/anonymousplanet-master.asc` for details
|
||||
**Fingerprint:** Verify from the repository's official documentation
|
||||
|
||||
---
|
||||
|
||||
*For questions or issues with verification, please open an issue on GitHub.*
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,39 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ "$1" == "" ]]; then
|
||||
# Sign PDF
|
||||
mkdir -p upload/
|
||||
echo "Calculating hashes..."
|
||||
sha256sum ./*.pdf >> ./upload/sha256sum.txt
|
||||
b2sum ./*.pdf >> ./upload/b2sum.txt
|
||||
sha256sum ./*.odt >> ./upload/sha256sum.txt
|
||||
b2sum ./*.odt >> ./upload/b2sum.txt
|
||||
echo "Calculated hashes. Signing generated files..."
|
||||
for f in ./*.pdf; do
|
||||
echo "Signing PDFs: $f"
|
||||
# verify with GPG
|
||||
gpg --default-key 83A6CF9EF57AC25B5C7F5D29285E6048A12321B2 --armor --detach-sign --sign "$f"
|
||||
# verify with `minisign -Vm <file> -P RWQ0WYJ07DUokK8V/6LNJ9bf/O/QM9k4FSlDmzgEeXm7lEpw3ecYjXDM`
|
||||
yes '' | minisign -S -s /home/user/.minisign/minisign.key -m "$f"
|
||||
done
|
||||
for f in ./*.odt; do
|
||||
echo "Signing ODTs: $f"
|
||||
gpg --default-key 83A6CF9EF57AC25B5C7F5D29285E6048A12321B2 --armor --detach-sign --sign "$f"
|
||||
yes '' | minisign -S -s /home/user/.minisign/minisign.key -m "$f"
|
||||
done
|
||||
echo "All files cryptographically signed."
|
||||
cp /home/user/KEY_ROTATION.md.42FF35DB9DE7C088AB0FD4A70C216A52F6DF4920.asc ./KEY_ROTATION.md.asc
|
||||
cp /home/user/KEY_ROTATION.md.902835EC74825934.minisig ./KEY_ROTATION.md.minisig
|
||||
echo "Done."
|
||||
exit
|
||||
fi
|
||||
|
||||
#bn="$1"
|
||||
#
|
||||
#echo "Generating HTML..."
|
||||
#pandoc --self-contained "$bn".md -o upload/"$bn".html --metadata title="The Hitchhiker's Guide to Online Anonymity"
|
||||
#echo "Generating PDF..."
|
||||
#pandoc --self-contained "$bn".md -o upload/"$bn".pdf --metadata title="The Hitchhiker's Guide to Online Anonymity" -t context
|
||||
#echo "Generating ODT..."
|
||||
#pandoc --self-contained "$bn".md -o upload/"$bn".odt --metadata title="The Hitchhiker's Guide to Online Anonymity"
|
||||
|
||||
+33
-19
@@ -1,4 +1,6 @@
|
||||
site_name: Hitchhiker’s Guide
|
||||
site_name: Hitchhiker's Guide
|
||||
site_author: Anonymous Planet
|
||||
site_description: "The comprehensive guide for online anonymity and OpSec."
|
||||
site_dir: '/site/'
|
||||
docs_dir: 'docs/'
|
||||
site_url: "https://www.anonymousplanet.org/"
|
||||
@@ -41,21 +43,23 @@ theme:
|
||||
|
||||
plugins:
|
||||
- social: {}
|
||||
- search: {}
|
||||
# macros: {}
|
||||
# meta: {}
|
||||
# git-latest-tag: {}
|
||||
- git-authors: {}
|
||||
# git-latest-release: {}
|
||||
# - with-pdf:
|
||||
- search:
|
||||
separator: '[\s\u200b\-_,:!=\[\]()"`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])'
|
||||
# - macros: {}
|
||||
# - meta: {}
|
||||
# - git-latest-tag: {}
|
||||
# - git-authors: {}
|
||||
# - git-latest-release: {}
|
||||
|
||||
extra_css:
|
||||
- stylesheets/extra.css
|
||||
|
||||
extra:
|
||||
social:
|
||||
- icon: simple/mastodon
|
||||
link: https://mastodon.social/@anonymousplanet
|
||||
name: Mastodon
|
||||
- icon: simple/matrix
|
||||
link: https://matrix.to/#/#p-s-a:matrix.org
|
||||
name: Matrix Space
|
||||
- icon: simple/gitlab
|
||||
link: http://wmj5kiic7b6kjplpbvwadnht2nh2qnkbnqtcv3dyvpqtz7ssbssftxid.onion/
|
||||
name: "0xacab"
|
||||
@@ -71,9 +75,9 @@ extra:
|
||||
- icon: simple/codeberg
|
||||
link: https://codeberg.org/anonymousplanet
|
||||
name: Codeberg
|
||||
# - icon: simple/torbrowser
|
||||
# link: TODO
|
||||
# name: Hidden service
|
||||
# - icon: simple/torbrowser
|
||||
# link: http://thgtoa3jzy3doku7hkna32htpghjijefscwvh4dyjgfydbbjkeiohgid.onion/
|
||||
# name: Hidden service
|
||||
|
||||
markdown_extensions:
|
||||
- pymdownx.highlight:
|
||||
@@ -88,8 +92,8 @@ markdown_extensions:
|
||||
- pymdownx.mark
|
||||
- attr_list
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||
emoji_generator: !!python/name:materialx.emoji.to_svg
|
||||
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
||||
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
||||
- pymdownx.superfences:
|
||||
custom_fences:
|
||||
- name: mermaid
|
||||
@@ -111,14 +115,24 @@ markdown_extensions:
|
||||
- md_in_html: {}
|
||||
- meta: {}
|
||||
- abbr: {}
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:materialx.emoji.twemoji
|
||||
emoji_generator: !!python/name:materialx.emoji.to_svg
|
||||
- tables: {}
|
||||
- footnotes: {}
|
||||
- toc:
|
||||
permalink: true
|
||||
toc_depth: 3
|
||||
|
||||
nav:
|
||||
- Home: index.md
|
||||
- About: about/index.md
|
||||
- Verify: verify/index.md
|
||||
- Guide:
|
||||
- guide/index.md
|
||||
- Workflow Documentation: guide/dev-workflow.md
|
||||
- Code: code/index.md
|
||||
- Contribute: contribute/index.md
|
||||
- Constitution: constitution/index.md
|
||||
- Mirrors: mirrors/index.md
|
||||
- Twitter: twitter/index.md
|
||||
|
||||
copyright: |
|
||||
© 2023 <a href="https://anonymousplanet.org/" target="_blank" rel="noopener">Anonymous Planet</a>
|
||||
© 2023-2026 <a href="https://anonymousplanet.org/" target="_blank" rel="noopener">Anonymous Planet</a>
|
||||
|
||||
+13
-3
@@ -1,13 +1,23 @@
|
||||
# Import
|
||||
|
||||
```
|
||||
```bash
|
||||
$ gpg --import pgp/core-devs/*
|
||||
```
|
||||
|
||||
# Verify
|
||||
|
||||
TODO
|
||||
```bash
|
||||
$ gpg --verify pgp/core-devs/than/than-crypto.txt
|
||||
|
||||
### All signing keys are signed by the Master Signing Key
|
||||
|
||||
gpg: Signature made Sat 19 Jul 2025 02:04:10 AM EDT
|
||||
gpg: using EDDSA key 8B3A74890536BAD50D9376EBF1CB32F67E3302A1
|
||||
gpg: Good signature from "nopenothinghere@proton.me <nopenothinghere@proton.me>" [ultimate]
|
||||
gpg: aka "Nope Nothing (Anonymous Planet Contact) <no@anonymousplanet.org>" [ultimate]
|
||||
gpg: aka "Nope Nothing (Systems Administrator) <admin@itsnothing.net>" [ultimate]
|
||||
Primary key fingerprint: 8B3A 7489 0536 BAD5 0D93 76EB F1CB 32F6 7E33 02A1
|
||||
```
|
||||
|
||||
## All signing keys are signed by the Master Signing Key
|
||||
|
||||
TODO
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA512
|
||||
|
||||
Tue Jul 25 14:51:36 EDT 2023
|
||||
|
||||
I am the admin of itsnothing.net (@Unknown@ioc.exchange) and co-admin of THGTOA.
|
||||
I will update this canary within 1 month.
|
||||
|
||||
Latest bitcoin block hash:
|
||||
00000000000000000000d9330bf8a03ce70cbe5542bddd16558693a43ea32fd3
|
||||
|
||||
I am in complete control of all my key material.
|
||||
|
||||
All previous keys have been revoked as part of standard OPSEC key rotation procedures.
|
||||
Do not encrypt communications to my old keys, I will not read them.
|
||||
|
||||
The key currently published on my website https://itsnothing.net/pgp.txt with a fingerprint
|
||||
of C87D87466FD205945CF10A3821AB6B6A6CB2C337, is my only PGP key for public communication.
|
||||
|
||||
Permanent record of old and new PGP keys:
|
||||
|
||||
the old key was:
|
||||
|
||||
pub rsa4096/0xB208C4084A2C582D 2022-11-04 [SC] [expires: 2027-11-03]
|
||||
Key fingerprint = D793 9998 F78B ADB5 18C1 B600 B208 C408 4A2C 582D
|
||||
uid [ultimate] Nope <no@anonymousplanet.org>
|
||||
|
||||
And the new key is:
|
||||
|
||||
pub ed25519/0x21AB6B6A6CB2C337 2023-07-14 [SC]
|
||||
Key fingerprint = C87D 8746 6FD2 0594 5CF1 0A38 21AB 6B6A 6CB2 C337
|
||||
uid [ultimate] nopenothinghere@proton.me <nopenothinghere@proton.me>
|
||||
|
||||
To fetch the full key, you can simply do:
|
||||
|
||||
gpg --keyserver keys.openpgp.org --recv-key 0x21AB6B6A6CB2C337
|
||||
|
||||
**
|
||||
Note: this keyserver is experimental.[0] I still have yet to add this key to
|
||||
the I2P keyserver pool, and I don't know if I will. If you have previously
|
||||
signed my key but did a local-only signature (lsign), you will not want to
|
||||
issue the following, instead you will want to use --lsign-key, and not send
|
||||
the signatures to the keyserver.
|
||||
**
|
||||
|
||||
gpg --sign-key 0x21AB6B6A6CB2C337
|
||||
|
||||
I'd like to receive your signatures on my key. You can either send me an e-mail
|
||||
with the new signatures (if you have a functional MTA on your system):
|
||||
|
||||
gpg --export 0x21AB6B6A6CB2C337 | gpg --encrypt -r 0x21AB6B6A6CB2C337 --armor \
|
||||
| mail -s 'OpenPGP Signatures' <nopenothinghere@proton.me>
|
||||
|
||||
Additionally, I highly recommend that you implement a mechanism to keep your key
|
||||
material up-to-date so that you obtain the latest revocations, and other updates
|
||||
in a timely manner. You can do regular key updates by using parcimonie[1] to
|
||||
refresh your keyring. Parcimonie is a daemon that slowly refreshes your keyring
|
||||
from a keyserver over Tor. It uses a randomized sleep, and fresh tor circuits
|
||||
for each key. The purpose is to make it hard for an attacker to correlate the
|
||||
key updates with your keyring.
|
||||
|
||||
I also highly recommend checking out the excellent Riseup GPG best practices
|
||||
doc, from which I stole most of the text for this transition message ;-)
|
||||
|
||||
https://we.riseup.net/riseuplabs+paow/openpgp-best-practices
|
||||
|
||||
Please let me know if you have any questions, or problems, and sorry for the
|
||||
inconvenience.
|
||||
|
||||
Nope (Anonymous Planet) <no@anonymousplanet.org>
|
||||
|
||||
0. https://gist.github.com/rjhansen/67ab921ffb4084c865b3618d6955275f
|
||||
1. https://directory.fsf.org/wiki/Parcimonie
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iHUEARYKAB0WIQTIfYdGb9IFlFzxCjghq2tqbLLDNwUCZMAZwAAKCRAhq2tqbLLD
|
||||
N3l3AQC28SZK5HHU1o7K36ifOd/OKj97urrMZF+NUkaRmAwQxgEAlIa2y9g0JoQW
|
||||
epEpViXFDwyWIUfNhVaJwUWjn/DLoAI=
|
||||
=A72C
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Experimental dark mode support.
|
||||
|
||||
This script builds both light and dark mode MkDocs site, then renders docs/guide/ to single PDFs via Chromium.
|
||||
|
||||
Usage:
|
||||
python scripts/build_guide_pdf.py # Generate light mode PDF only
|
||||
python scripts/build_guide_pdf.py --dark-mode # Generate dark mode PDF only
|
||||
python scripts/build_guide_pdf.py --both # Generate both light and dark mode PDFs
|
||||
|
||||
Examples:
|
||||
python scripts/build_guide_pdf.py --site-dir build/html --pdf-light export/thgtoa.pdf
|
||||
python scripts/build_guide_pdf.py --dark-mode --pdf-dark export/thgtoa-dark.pdf
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def repo_root() -> Path:
|
||||
return Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def find_chromium_executable() -> Path | None:
|
||||
if sys.platform == "win32":
|
||||
paths = [
|
||||
Path(os.environ.get("PROGRAMFILES(X86)", "")) / "Microsoft/Edge/Application/msedge.exe",
|
||||
Path(os.environ.get("LOCALAPPDATA", "")) / "Microsoft/Edge/Application/msedge.exe",
|
||||
Path(os.environ.get("PROGRAMFILES", "")) / "Google/Chrome/Application/chrome.exe",
|
||||
Path(os.environ.get("PROGRAMFILES(X86)", "")) / "Google/Chrome/Application/chrome.exe",
|
||||
Path(os.environ.get("LOCALAPPDATA", "")) / "Google/Chrome/Application/chrome.exe",
|
||||
]
|
||||
for p in paths:
|
||||
if p.is_file():
|
||||
return p
|
||||
for name in ("chrome", "msedge"):
|
||||
w = shutil.which(name)
|
||||
if w:
|
||||
return Path(w)
|
||||
elif sys.platform == "darwin":
|
||||
for p in (
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge",
|
||||
"/Applications/Chromium.app/Contents/MacOS/Chromium",
|
||||
):
|
||||
if os.path.isfile(p):
|
||||
return Path(p)
|
||||
for name in ("google-chrome-stable", "google-chrome", "chromium-browser", "chromium", "chrome"):
|
||||
w = shutil.which(name)
|
||||
if w:
|
||||
return Path(w)
|
||||
return None
|
||||
|
||||
|
||||
def run_mkdocs(site_dir: Path) -> None:
|
||||
site_dir.mkdir(parents=True, exist_ok=True)
|
||||
subprocess.run(
|
||||
[sys.executable, "-m", "mkdocs", "build", "-d", str(site_dir)],
|
||||
cwd=repo_root(),
|
||||
check=True,
|
||||
)
|
||||
|
||||
|
||||
def print_to_pdf(browser: Path, html_file: Path, pdf_out: Path, dark_mode: bool = False) -> Path:
|
||||
"""Write PDF to ``pdf_out``. Uses a temp file first so an open ``guide.pdf`` on Windows
|
||||
does not block the build: if the final path is locked, writes ``guide-new.pdf`` instead.
|
||||
|
||||
Args:
|
||||
browser: Path to Chromium executable
|
||||
html_file: Path to HTML file to convert
|
||||
pdf_out: Output PDF path
|
||||
dark_mode: If True, use dark mode color scheme via --prefers-color-scheme flag
|
||||
"""
|
||||
pdf_out.parent.mkdir(parents=True, exist_ok=True)
|
||||
partial = pdf_out.parent / f".{pdf_out.name}.writing"
|
||||
partial.unlink(missing_ok=True)
|
||||
|
||||
uri = html_file.resolve().as_uri()
|
||||
|
||||
# Chromium headless print; allow time for fonts/images on very large pages.
|
||||
cmd = [str(browser)]
|
||||
if os.environ.get("CI"):
|
||||
# GitHub Actions / other CI runners often need these for Chromium to start.
|
||||
cmd += [
|
||||
"--no-sandbox",
|
||||
"--disable-setuid-sandbox",
|
||||
"--disable-dev-shm-usage",
|
||||
]
|
||||
|
||||
cmd += [
|
||||
"--headless=new",
|
||||
"--disable-gpu",
|
||||
"--no-pdf-header-footer",
|
||||
]
|
||||
|
||||
# Add dark mode preference if requested
|
||||
if dark_mode:
|
||||
cmd.append("--prefers-color-scheme=dark")
|
||||
|
||||
cmd += [
|
||||
f"--print-to-pdf={partial.resolve()}",
|
||||
uri,
|
||||
]
|
||||
|
||||
subprocess.run(cmd, check=True, timeout=600)
|
||||
deadline = time.time() + 120
|
||||
while time.time() < deadline:
|
||||
if partial.exists() and partial.stat().st_size > 0:
|
||||
break
|
||||
time.sleep(0.25)
|
||||
else:
|
||||
partial.unlink(missing_ok=True)
|
||||
raise RuntimeError(f"PDF was not written to {partial}")
|
||||
|
||||
try:
|
||||
if pdf_out.exists():
|
||||
pdf_out.unlink()
|
||||
except PermissionError:
|
||||
fallback = pdf_out.with_name(f"{pdf_out.stem}-new{pdf_out.suffix}")
|
||||
fallback.unlink(missing_ok=True)
|
||||
partial.replace(fallback)
|
||||
return fallback
|
||||
|
||||
partial.replace(pdf_out)
|
||||
return pdf_out
|
||||
|
||||
|
||||
def generate_dark_mode_html(html_file: Path, output_file: Path, dark_css_path: Path) -> None:
|
||||
"""Create a temporary HTML file with dark mode stylesheet applied.
|
||||
|
||||
This is used when we need to force dark mode rendering via CSS rather than browser flags.
|
||||
"""
|
||||
try:
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
# Read the original HTML
|
||||
html_content = html_file.read_text(encoding='utf-8')
|
||||
soup = BeautifulSoup(html_content, 'html.parser')
|
||||
|
||||
# Add dark mode stylesheet link if not present
|
||||
existing_links = [link.get('href', '') for link in soup.find_all('link', rel='stylesheet')]
|
||||
if not any(dark_css_path.name in link for link in existing_links):
|
||||
head = soup.head or soup.new_tag('head')
|
||||
link_tag = soup.new_tag('link', rel='stylesheet', href=str(dark_css_path))
|
||||
if soup.head:
|
||||
soup.head.append(link_tag)
|
||||
else:
|
||||
# Create a new head section
|
||||
new_head = soup.new_tag('head')
|
||||
new_head.append(link_tag)
|
||||
soup.insert(0, new_head)
|
||||
|
||||
# Write the modified HTML
|
||||
output_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
output_file.write_text(str(soup), encoding='utf-8')
|
||||
except ImportError:
|
||||
print("BeautifulSoup not available. Skipping CSS injection.")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
root = repo_root()
|
||||
ap = argparse.ArgumentParser(description="Build MkDocs + single-page guide PDF (light and/or dark mode).")
|
||||
ap.add_argument(
|
||||
"--site-dir",
|
||||
type=Path,
|
||||
default=root / "build" / "html",
|
||||
help="MkDocs output directory (default: ./build/html)",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--pdf-light",
|
||||
type=Path,
|
||||
default=root / "export" / "thgtoa.pdf",
|
||||
help="Output PDF path for light mode (default: ./export/guide.pdf)",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--pdf-dark",
|
||||
type=Path,
|
||||
default=root / "export" / "thgtoa-dark.pdf",
|
||||
help="Output PDF path for dark mode (default: ./export/guide-dark.pdf)",
|
||||
)
|
||||
ap.add_argument("--skip-mkdocs", action="store_true", help="Reuse existing site dir; only run print-to-pdf.")
|
||||
ap.add_argument("--dark-mode", action="store_true", help="Generate dark mode PDF only")
|
||||
ap.add_argument("--both", action="store_true", help="Generate both light and dark mode PDFs")
|
||||
args = ap.parse_args()
|
||||
|
||||
# Determine which modes to generate
|
||||
if args.dark_mode:
|
||||
modes = ["dark"]
|
||||
elif args.both:
|
||||
modes = ["light", "dark"]
|
||||
else:
|
||||
modes = ["light"]
|
||||
|
||||
guide_html = args.site_dir / "guide" / "index.html"
|
||||
|
||||
if not args.skip_mkdocs or any(mode == "light" for mode in modes):
|
||||
run_mkdocs(args.site_dir)
|
||||
|
||||
if not guide_html.is_file():
|
||||
print(f"Missing {guide_html}; run without --skip-mkdocs first.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
browser = find_chromium_executable()
|
||||
if not browser:
|
||||
print(
|
||||
"No Chromium-based browser found (Chrome, Edge, or Chromium). "
|
||||
"Install Google Chrome or Microsoft Edge, or add Chromium to PATH.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
dark_css_path = root / "docs" / "stylesheets" / "dark-extra.css"
|
||||
|
||||
# Generate light mode PDF (default)
|
||||
if "light" in modes:
|
||||
out_light = print_to_pdf(browser, guide_html, args.pdf_light, dark_mode=False)
|
||||
size_kb = out_light.stat().st_size // 1024
|
||||
print(f"Wrote {out_light.resolve()} ({size_kb} KiB) [Light Mode]")
|
||||
if out_light.resolve() != args.pdf_light.resolve():
|
||||
print(
|
||||
f"Note: {args.pdf_light.name} was in use; close it and rename or replace with the file above.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
# Generate dark mode PDF
|
||||
if "dark" in modes:
|
||||
out_dark = print_to_pdf(browser, guide_html, args.pdf_dark, dark_mode=True)
|
||||
size_kb = out_dark.stat().st_size // 1024
|
||||
print(f"Wrote {out_dark.resolve()} ({size_kb} KiB) [Dark Mode]")
|
||||
if out_dark.resolve() != args.pdf_dark.resolve():
|
||||
print(
|
||||
f"Note: {args.pdf_dark.name} was in use; close it and rename or replace with the file above.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,322 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Setup helper for PDF workflow configuration.
|
||||
|
||||
This script helps you configure the necessary GitHub Secrets for the automated
|
||||
PDF build, signing, and VirusTotal scanning workflows.
|
||||
|
||||
Usage:
|
||||
python scripts/setup_workflow.py
|
||||
|
||||
Requirements:
|
||||
- Python 3.8+
|
||||
- GPG installed (for key export)
|
||||
- Access to GitHub repository settings
|
||||
|
||||
What it does:
|
||||
1. Validates your GPG key setup
|
||||
2. Exports the public key for verification
|
||||
3. Provides instructions for adding secrets to GitHub
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def repo_root() -> Path:
|
||||
return Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def check_gpg_installed() -> bool:
|
||||
"""Check if GPG is installed and accessible."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["gpg", "--version"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10,
|
||||
)
|
||||
return result.returncode == 0
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
return False
|
||||
|
||||
|
||||
def list_gpg_keys() -> list[dict]:
|
||||
"""List all GPG keys in the keyring."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["gpg", "--list-keys", "--with-colons"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
keys = []
|
||||
current_key = {}
|
||||
|
||||
for line in result.stdout.split('\n'):
|
||||
if line.startswith('pub:'):
|
||||
if current_key:
|
||||
keys.append(current_key)
|
||||
parts = line.split(':')
|
||||
current_key = {
|
||||
'type': parts[1],
|
||||
'key_id': parts[4],
|
||||
'fingerprint': parts[9] if len(parts) > 9 else None,
|
||||
'created': parts[5],
|
||||
'expires': parts[6],
|
||||
'uid': None,
|
||||
}
|
||||
elif line.startswith('uid:'):
|
||||
parts = line.split(':')
|
||||
current_key['uid'] = parts[9] if len(parts) > 9 else None
|
||||
|
||||
if current_key:
|
||||
keys.append(current_key)
|
||||
|
||||
return keys
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error listing GPG keys: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def export_public_key(key_id: str, output_file: Path | None = None) -> str | None:
|
||||
"""Export a public key in ASCII armor format."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["gpg", "--armor", "--export", key_id],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
if output_file:
|
||||
output_file.write_text(result.stdout)
|
||||
print(f"✓ Public key exported to {output_file}")
|
||||
|
||||
return result.stdout
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error exporting public key: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def export_private_key(key_id: str, output_file: Path | None = None) -> str | None:
|
||||
"""Export a private key in ASCII armor format (requires passphrase)."""
|
||||
try:
|
||||
# This will prompt for passphrase interactively
|
||||
result = subprocess.run(
|
||||
["gpg", "--armor", "--export-secret-keys", key_id],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
if output_file:
|
||||
output_file.write_text(result.stdout)
|
||||
print(f"✓ Private key exported to {output_file}")
|
||||
|
||||
return result.stdout
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error exporting private key: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def validate_gpg_key(key_id: str) -> bool:
|
||||
"""Validate that a GPG key has signing capability."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["gpg", "--list-keys", "--with-colons", key_id],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
|
||||
# Check for 's' (signing) in the pub line
|
||||
for line in result.stdout.split('\n'):
|
||||
if line.startswith('pub:'):
|
||||
flags = line.split(':')[1]
|
||||
return 's' in flags
|
||||
|
||||
return False
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
return False
|
||||
|
||||
|
||||
def print_setup_instructions():
|
||||
"""Print instructions for configuring GitHub Secrets."""
|
||||
print("\n" + "="*70)
|
||||
print("GITHUB SECRETS SETUP INSTRUCTIONS")
|
||||
print("="*70)
|
||||
|
||||
print("""
|
||||
To enable the automated PDF workflow, you need to add three secrets to your
|
||||
GitHub repository:
|
||||
|
||||
1. GPG_PRIVATE_KEY
|
||||
- Your GPG private key in ASCII armor format
|
||||
- Used to sign PDFs and hash files
|
||||
- IMPORTANT: Keep this secret! Never commit it publicly
|
||||
|
||||
2. GPG_PASSPHRASE
|
||||
- The passphrase for your GPG private key
|
||||
- Required to unlock the private key for signing
|
||||
|
||||
3. VT_API_KEY (optional but recommended)
|
||||
- VirusTotal API key for malware scanning
|
||||
- Get a free key at: https://www.virustotal.com/gui/join-us
|
||||
|
||||
|
||||
HOW TO ADD SECRETS:
|
||||
|
||||
1. Go to your repository on GitHub
|
||||
2. Click 'Settings' → 'Secrets and variables' → 'Actions'
|
||||
3. Click 'New repository secret' for each secret below:
|
||||
|
||||
Secret Name | Value Format
|
||||
---------------------|--------------------------------------------------
|
||||
GPG_PRIVATE_KEY | Paste the entire ASCII armored key (BEGIN PGP...)
|
||||
GPG_PASSPHRASE | Your key's passphrase (no special characters issues)
|
||||
VT_API_KEY | Your VirusTotal API key
|
||||
|
||||
|
||||
VERIFYING YOUR SETUP:
|
||||
|
||||
After adding secrets, you can test by:
|
||||
1. Going to 'Actions' tab
|
||||
2. Selecting 'Build guide PDF' workflow
|
||||
3. Clicking 'Run workflow'
|
||||
4. Checking if the workflow completes successfully
|
||||
|
||||
|
||||
TROUBLESHOOTING:
|
||||
|
||||
- If GPG signing fails: Check that your key has signing capability ('s' flag)
|
||||
- If passphrase is wrong: Verify you're using the correct passphrase
|
||||
- If VT scan fails: Ensure API key is valid and within rate limits
|
||||
|
||||
|
||||
SECURITY NOTES:
|
||||
|
||||
⚠ NEVER share your private key or passphrase publicly
|
||||
⚠ Always use repository secrets, never hardcode in scripts
|
||||
⚠ Rotate keys periodically if compromised
|
||||
⚠ Use strong passphrases (12+ characters recommended)
|
||||
""")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print("\n" + "="*70)
|
||||
print("PDF WORKFLOW SETUP HELPER")
|
||||
print("="*70)
|
||||
|
||||
# Check GPG installation
|
||||
if not check_gpg_installed():
|
||||
print("⚠ WARNING: GPG is not installed or not in PATH")
|
||||
print("Please install GPG before continuing:")
|
||||
print(" - Linux: sudo apt install gnupg")
|
||||
print(" - macOS: brew install gnupg")
|
||||
print(" - Windows: https://www.gpg4win.org/")
|
||||
print("\nContinuing anyway...")
|
||||
|
||||
# List available keys
|
||||
print("\n🔑 Available GPG Keys:")
|
||||
print("-" * 70)
|
||||
|
||||
keys = list_gpg_keys()
|
||||
|
||||
if not keys:
|
||||
print("No GPG keys found in your keyring.")
|
||||
print("Generate a key with: gpg --full-generate-key")
|
||||
return 1
|
||||
|
||||
for i, key in enumerate(keys, 1):
|
||||
status = "✓" if validate_gpg_key(key['key_id']) else "✗"
|
||||
print(f"\n{i}. {status} Key ID: {key['key_id']}")
|
||||
print(f" Fingerprint: {key.get('fingerprint', 'N/A')}")
|
||||
print(f" UID: {key.get('uid', 'Unknown')}")
|
||||
print(f" Created: {key.get('created', 'Unknown')}")
|
||||
|
||||
if key.get('expires'):
|
||||
print(f" Expires: {key['expires']}")
|
||||
|
||||
# Ask user to select key
|
||||
print("\n" + "-" * 70)
|
||||
try:
|
||||
choice = input("\nEnter the number of the key you want to use (1-{}): ".format(len(keys)))
|
||||
selected_index = int(choice) - 1
|
||||
|
||||
if not (0 <= selected_index < len(keys)):
|
||||
print("Invalid selection!")
|
||||
return 1
|
||||
|
||||
except ValueError:
|
||||
print("Invalid input! Please enter a number.")
|
||||
return 1
|
||||
|
||||
selected_key = keys[selected_index]
|
||||
|
||||
# Validate key has signing capability
|
||||
if not validate_gpg_key(selected_key['key_id']):
|
||||
print(f"\n⚠ WARNING: Selected key does not have signing capability!")
|
||||
print("You need a key with 's' (signing) flag for PDF signatures.")
|
||||
confirm = input("Continue anyway? (y/N): ")
|
||||
if confirm.lower() != 'y':
|
||||
return 1
|
||||
|
||||
# Export public key
|
||||
print(f"\n📤 Exporting public key for {selected_key['uid']}...")
|
||||
public_key_file = repo_root() / "pgp" / "workflow-public.asc"
|
||||
|
||||
public_key = export_public_key(selected_key['key_id'], public_key_file)
|
||||
|
||||
if not public_key:
|
||||
print("Failed to export public key!")
|
||||
return 1
|
||||
|
||||
# Show public key info
|
||||
print("\n✓ Public Key Information:")
|
||||
print("-" * 70)
|
||||
for line in public_key.split('\n')[:5]:
|
||||
print(line)
|
||||
print("...")
|
||||
|
||||
# Instructions for private key export
|
||||
print("\n🔐 Private Key Export:")
|
||||
print("-" * 70)
|
||||
print("""
|
||||
To get your private key for the GPG_PRIVATE_KEY secret:
|
||||
|
||||
1. Run this command (you'll be prompted for passphrase):
|
||||
gpg --armor --export-secret-keys {} > workflow-private.asc
|
||||
|
||||
2. Copy the ENTIRE output including BEGIN and END lines
|
||||
|
||||
3. Add it to GitHub Secrets as 'GPG_PRIVATE_KEY'
|
||||
|
||||
⚠ IMPORTANT: Keep your private key secure! Never commit it publicly.
|
||||
""".format(selected_key['key_id']))
|
||||
|
||||
# Print setup instructions
|
||||
print_setup_instructions()
|
||||
|
||||
print("\n" + "="*70)
|
||||
print("SETUP COMPLETE!")
|
||||
print("="*70)
|
||||
print(f"\nPublic key saved to: {public_key_file}")
|
||||
print("Next steps:")
|
||||
print("1. Export your private key (see instructions above)")
|
||||
print("2. Add all three secrets to GitHub repository settings")
|
||||
print("3. Test the workflow by triggering a manual build")
|
||||
print("\nFor more information, see: docs/guide/pdf-workflow.md\n")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Verification script for PDF files.
|
||||
|
||||
This script verifies:
|
||||
1. SHA256 hash integrity of PDF files
|
||||
2. GPG signature authenticity
|
||||
3. VirusTotal scan status (optional)
|
||||
|
||||
Usage:
|
||||
python scripts/verify_pdf.py --all # Verify everything
|
||||
python scripts/verify_pdf.py --hashes # Only verify hashes
|
||||
python scripts/verify_pdf.py --signatures # Only verify signatures
|
||||
python scripts/verify_pdf.py --vt # Check VT status (requires API key)
|
||||
|
||||
Examples:
|
||||
python scripts/verify_pdf.py --all
|
||||
python scripts/verify_pdf.py --hashes --file export/thgtoa.pdf
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def repo_root() -> Path:
|
||||
return Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
def calculate_sha256(file_path: Path) -> str:
|
||||
"""Calculate SHA256 hash of a file."""
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(file_path, "rb") as f:
|
||||
for byte_block in iter(lambda: f.read(4096), b""):
|
||||
sha256_hash.update(byte_block)
|
||||
return sha256_hash.hexdigest()
|
||||
|
||||
|
||||
def verify_hash(file_path: Path, expected_hash: str) -> bool:
|
||||
"""Verify file hash against expected value."""
|
||||
actual_hash = calculate_sha256(file_path)
|
||||
is_valid = actual_hash == expected_hash
|
||||
status = "✓ PASS" if is_valid else "✗ FAIL"
|
||||
print(f"{status}: {file_path.name}")
|
||||
print(f" Expected: {expected_hash}")
|
||||
print(f" Actual: {actual_hash}")
|
||||
return is_valid
|
||||
|
||||
|
||||
def verify_signature(file_path: Path, sig_file: Path) -> bool:
|
||||
"""Verify GPG signature of a file."""
|
||||
if not sig_file.exists():
|
||||
print(f"✗ FAIL: Signature file not found: {sig_file}")
|
||||
return False
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["gpg", "--verify", str(sig_file), str(file_path)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
print(f"✓ PASS: {file_path.name} signature verified")
|
||||
# Extract key info from GPG output
|
||||
for line in result.stdout.split('\n'):
|
||||
if 'Good signature' in line or 'key ID' in line.lower():
|
||||
print(f" {line.strip()}")
|
||||
return True
|
||||
else:
|
||||
print(f"✗ FAIL: {file_path.name} signature verification failed")
|
||||
print(f" Error: {result.stderr}")
|
||||
return False
|
||||
|
||||
except FileNotFoundError:
|
||||
print("⚠ WARNING: GPG not installed. Skipping signature verification.")
|
||||
return None
|
||||
|
||||
|
||||
def verify_from_hash_file(file_path: Path, hash_file: Path) -> bool:
|
||||
"""Verify file hash from a hash file."""
|
||||
if not hash_file.exists():
|
||||
print(f"✗ FAIL: Hash file not found: {hash_file}")
|
||||
return False
|
||||
|
||||
expected_hash = None
|
||||
with open(hash_file, 'r') as f:
|
||||
for line in f:
|
||||
parts = line.strip().split()
|
||||
if len(parts) >= 2 and parts[1] == str(file_path):
|
||||
expected_hash = parts[0]
|
||||
break
|
||||
|
||||
if not expected_hash:
|
||||
print(f"✗ FAIL: Hash not found in {hash_file.name} for {file_path.name}")
|
||||
return False
|
||||
|
||||
return verify_hash(file_path, expected_hash)
|
||||
|
||||
|
||||
def check_virustotal(file_hash: str, api_key: str | None = None) -> dict | None:
|
||||
"""Check VirusTotal scan status for a file hash."""
|
||||
if not api_key:
|
||||
print("⚠ WARNING: VT_API_KEY not set. Skipping VirusTotal check.")
|
||||
return None
|
||||
|
||||
try:
|
||||
import urllib.request
|
||||
import json
|
||||
|
||||
url = f"https://www.virustotal.com/api/v3/files/{file_hash}"
|
||||
request = urllib.request.Request(url, headers={"x-apikey": api_key})
|
||||
|
||||
with urllib.request.urlopen(request, timeout=30) as response:
|
||||
data = json.loads(response.read().decode())
|
||||
|
||||
stats = data.get('data', {}).get('attributes', {}).get('last_analysis_stats', {})
|
||||
total = sum(stats.values()) if stats else 0
|
||||
|
||||
print(f"\n🦠 VirusTotal Results for {file_hash[:16]}...")
|
||||
print(f" Total scans: {total}")
|
||||
|
||||
if stats:
|
||||
print(f" Malicious: {stats.get('malicious', 0)}")
|
||||
print(f" Suspicious: {stats.get('suspicious', 0)}")
|
||||
print(f" Undetected: {stats.get('undetected', 0)}")
|
||||
print(f" Clean: {stats.get('harmless', 0)}")
|
||||
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
print(f"⚠ ERROR checking VirusTotal: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
root = repo_root()
|
||||
ap = argparse.ArgumentParser(description="Verify PDF files (hashes, signatures, VT).")
|
||||
|
||||
# File paths
|
||||
ap.add_argument(
|
||||
"--light-pdf",
|
||||
type=Path,
|
||||
default=root / "export" / "thgtoa.pdf",
|
||||
help="Light mode PDF file",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--dark-pdf",
|
||||
type=Path,
|
||||
default=root / "export" / "thgtoa-dark.pdf",
|
||||
help="Dark mode PDF file",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--hash-file",
|
||||
type=Path,
|
||||
default=root / "sha256sum-light.txt",
|
||||
help="Hash file to verify against",
|
||||
)
|
||||
|
||||
# Verification modes
|
||||
group = ap.add_mutually_exclusive_group()
|
||||
group.add_argument("--all", action="store_true", help="Verify everything")
|
||||
group.add_argument("--hashes", action="store_true", help="Only verify hashes")
|
||||
group.add_argument("--signatures", action="store_true", help="Only verify signatures")
|
||||
ap.add_argument("--vt", action="store_true", help="Check VirusTotal status")
|
||||
|
||||
args = ap.parse_args()
|
||||
|
||||
# Determine what to verify
|
||||
if not any([args.all, args.hashes, args.signatures, args.vt]):
|
||||
args.all = True
|
||||
|
||||
all_passed = True
|
||||
|
||||
pdf_files = [
|
||||
("Light", args.light_pdf),
|
||||
("Dark", args.dark_pdf),
|
||||
]
|
||||
|
||||
for mode_name, pdf_file in pdf_files:
|
||||
if not pdf_file.exists():
|
||||
print(f"⚠ WARNING: {pdf_file.name} not found. Skipping.")
|
||||
continue
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Verifying {mode_name} PDF: {pdf_file.name}")
|
||||
print('='*60)
|
||||
|
||||
# Verify hash if requested
|
||||
if args.all or args.hashes:
|
||||
if not verify_from_hash_file(pdf_file, args.hash_file):
|
||||
all_passed = False
|
||||
|
||||
# Verify signature if requested
|
||||
if args.all or args.signatures:
|
||||
sig_file = pdf_file.with_suffix(pdf_file.suffix + ".sig")
|
||||
result = verify_signature(pdf_file, sig_file)
|
||||
if result is False: # None means skipped (GPG not installed)
|
||||
all_passed = False
|
||||
|
||||
# Check VirusTotal if requested
|
||||
if args.all or args.vt:
|
||||
file_hash = calculate_sha256(pdf_file)
|
||||
api_key = os.environ.get("VT_API_KEY")
|
||||
check_virustotal(file_hash, api_key)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
if all_passed:
|
||||
print("✓ All verifications PASSED")
|
||||
return 0
|
||||
else:
|
||||
print("✗ Some verifications FAILED")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -1,51 +0,0 @@
|
||||
37ee0be61ec1fc1ad1015434b218cf579896a0b361dba5d4043f818154ffbeb13339577e602e4c4d16e18b9293af9e2363304dd0852d82851acef192b4926636 ./CHANGELOG.html
|
||||
4bf2f96f5d452e0e18a2cc296742ed0f148b5da648433dd7f66da136aa29438f26f9c4ad4848663bc67cf43edbe000eba1666ceaffddc837c085c2e65620bc5c ./CHANGELOG.odt
|
||||
d53a134f76053a5743a9ea6cba24248553e9cccefe808c439010c930f3a25f4f9657d901124d6726002f78be76ebaebdf523782375de22d02506bec28ada603b ./CHANGELOG.pdf
|
||||
edc68f24affae9b45de3e98fbfae86350df5baae00f747fe06ac58bb8dbece7aff5acda3464ecf43b35acea036bef334b8475f438c035f5c9cc9ee2fb6920517 ./CODE_OF_CONDUCT.html
|
||||
86edda3a12c020c25318b387397bab3da138f28d6035633e7bd78edaccf3a27a8ee363797cc2036ad9c02bbce36bbe9e7f535928c3491dab55af26aaccd5b7f3 ./CODE_OF_CONDUCT.odt
|
||||
adb0cb9e7a2728b7cdf3f748234d98bccb2fbd048bea2014f1dc453bb786f490938999b9a321759f1d6cb541215a4bd72aecc70b15dcc8b0272bc5a4b4946999 ./CODE_OF_CONDUCT.pdf
|
||||
e1a0325b5d2b03f3e0d0ab1eb74cc61fa4fb714bec024f5e549bcd19031e0e52bdf4eaf7954698f532ddd9c5889acd51f2fec61bbcd3077f82d581bd0e9f8a93 ./CONTRIBUTING.html
|
||||
48fe31e26fbd9475e230ecc9e8d6c743281ce2658a71bda2cbd14bf3fc529161ae3a5610738946a4c1747b017c18c21c65728e54c4bbe66e42a84f14c6a46503 ./CONTRIBUTING.odt
|
||||
7e9282cbdeeecee8d72669a9de23a31a184f35ea448ff6418361197cea608fbdb25643f137c9d252d9f1f3ff320136bc47ebb4f1051b8e19d28c220bc99775da ./CONTRIBUTING.pdf
|
||||
e5608ee5d273ca761f7d8a1a6dbf5ce26d9bdb5cb2bb17a8b115ab0d970c04183f55c541e457a6bdbaaf89fe0233861021b9c7a4af6f08219359c158483ed10b ./KEY_ROTATION.html
|
||||
a1ef73572adf507d167c9a538687bbfc28efc03b0e25eb8d0d1f50a80dd83b4941edde3abf2c6cf9fe1d50eec7b8a61e6bb563c08e5528e16b0c472bb49dda2d ./KEY_ROTATION.odt
|
||||
ebb2dfdae69f54e1df8f9633963a930e65b3fd1075fe02633958d677351982b3463e1b1f7fdbf9c06a419ac324117d57cb1ce27da9735344411065d9a99c9b4e ./KEY_ROTATION.pdf
|
||||
61b55b1507341fc1355c6f9d757350f4774007ff949cfaa90d353e608a2aaff82019139d54bca91d474890f53163c5e795686175449c6c7f7327294ab523a066 ./LICENSE.html
|
||||
db5d374972d7c3c49ddee1816912a5262836a0c0e590c4f82fbfae0f76d0c648faced8a0336a2000fa38f3211d0c6db05e3a7fd0854a3c8669c267a2054446cc ./LICENSE.odt
|
||||
4e4d2e11b72b2c5ca773f7996a3f2022275a998a41685bcade8206e9906f0002e37248295de8618ebcb2d234ff56b8ecb6b75ee966d8699fec19e8d9c208fc3d ./LICENSE.pdf
|
||||
70fcd83ac062429f3c9c46cf3d6c19e37e2e125b4695d16207df155120d1c800fab87cd943e3c5e8d9e0a053539f1a99a7a292891ea8f50abd8ad8fc93cff12f ./README.html
|
||||
1783f71fb830fbfdea97ecb19b46a5cb3448b33bc93955bea796b9f9d4b50df52fd78c4e7e5233750aca3740c5280cc4e26293f26992448c85fea329c153aa29 ./README.odt
|
||||
4c7742719b5dab697e320a5e93843f459b494e34603010d2f746241bc42147d27cd67409cfe6b3ae3a73e757ba792f89da837a41461962c2f286e1b0b1fa8aa3 ./README.pdf
|
||||
1406eccdba99b061667d0469049a5e19d16f9bfa03447c84d2b6829e3cf4f193dd10baf8696fefce507faceb124921ff6a39bdf774e4094a70902eaaa7864271 ./about.html
|
||||
477702b22444d2876f225cf93655c7a52e816f8abafe6038ebed500d94001abe0e53ab80c83d35702fd96ec43a433240c960499533501cbdaf3156ddd859ea53 ./about.odt
|
||||
059bbbce8774287235fe154624dc5305916ba289488b09286556e8a8ec7c779f98d17fa5b936c0d99fae5e0c4c16fece46cd7245e9c37cb92ab4e805f878f4c4 ./about.pdf
|
||||
41a70d04322d7c2a115609587ff171026055511d521a5ae73d3fb19aed9ab1bcac9708b038780b9ebadcdeb919496e69c4424377e5d6b6d6447f032cfc0d4ab4 ./briar.html
|
||||
03667187bc43bf85b09b08805b0f806f208295a407663bda0803fb0a88dde11d67b540592ae976a078143db0c177c5176b957cc69240098d1b0b1812960b3923 ./briar.odt
|
||||
909fc17b1ab9e524c92e5519c1c95ebbfe0e12cff52374ffd4ecd5bb69b6f61e59a1a8c4f3e3047b9415282b23f5254e6db79c2e0e89bc588683806b68d7e7a8 ./briar.pdf
|
||||
c84a99f05fd0837a12561cef0bfae22b348dca7daaf3499d10324f40382e88b9e89dcd7fc04ca565e83438094987ddd144bc9707eea97918ef54178c3fef808a ./chatrooms-rules.html
|
||||
6c1c5a798239864d8ee7dc538199016178e6d49f5c250441d2dcee8351dbaa81217934eb5a042972793bec428ff7a602c9e9f8291f8233f7d863da91fa3b6d02 ./chatrooms-rules.odt
|
||||
dc30249ac1e551d6d011cd0ee6fed1f69c5a35601d77217480ced3957acb4a9e6d377b3f8f4887c4a05906f8bbad1f8bd4430eed48c3727ccd0cf8305b262c34 ./constitution.html
|
||||
14325fcc4f4f2bebbd81bc8772c98d7b284fcfc0c2bed79aef851f3e85d61b2d0501ce68cea1062903409da14b2ec3deb69f63a34e9cde3a1cd85a1243b4827d ./constitution.odt
|
||||
246beceea5f978e4b58f4976a5e130f0084de688bf258ea0731041a94c96c2aab7b0a60d969686dac3b8c7783a322eeab139ee01e59a9432898755329f2638c4 ./constitution.pdf
|
||||
d2162a0c52e03b4bdf00533ff1c1388b42ac81dae74c376221e2c08e6bd7a6c2935c987b43869d378beeabe20cc6f23672c66d012d1db9d79ae9d82ca2c0b7be ./donations.html
|
||||
0ad76fcfad374d097560bbe3fb5a5b7926557dfe01289ce882479c26931f45c5f813bac4cedcc7e45dee56c66f43d425af550e5a479e1e5e1ae750dd4c87b341 ./donations.odt
|
||||
0b535847a0479b4ba280d9b32074fa49a010ce453ceee3726078234b9466ef12b3ee36045a7145652b725a125c7b55525334c752561bd309215c05c64fd8fa11 ./donations.pdf
|
||||
96c479b5e9c06850bff0ba80cd661da6d0a29ac84fa941f02962dedf7ab58ba8e93896ca79aca07ad04ac64a8d34c079036754a167466ee50316d0255c468c18 ./guide.html
|
||||
9894e9f03768fb8104d5a71f642dac5a4681176914b38f5b46ec4dbaa906fdd956b79151d5534c06a7e52ce75837e3f4812d10888786a3aad95b624611975d95 ./guide.odt
|
||||
cf180738c66a47b55ac080497b8e3d6c658ff5e9e3f01d5b8efcff94171b50d6f173ac1c937ac80229e50d0b82df948f466c07cb7b00fe5e5c9a61e67427d201 ./guide.pdf
|
||||
011d1c7a16e42d0ff9c9ca3bb9583e968b9f89c5ff975d1d33255a4b547a475f6f9c858138990283acf52b4ad7da0ec6a0245e6f580a739f24aa9b958c6b3127 ./legacy.html
|
||||
bc8ce28cfc58e66068da29a53f7266d2f4c9247cd0b0854860af405e6b3a525592db6007377a152c23e68cbfa1df6cb11a06666036cbb2c45a5fef70f2fa9e4f ./legacy.odt
|
||||
1006e157a3c58072881f255bf5967b963aa11175eaa2a0527f868bad59e89faa65a4c0d22bb8d7835e7d7292112e9cadfd3aa411f1438766d0b24e04bd20ffc6 ./legacy.pdf
|
||||
76958f3298f9ac581e778a41a765090206da3140c465d20db120f6ed4760ebc21670fa58403b8f839b2b38f36f9ff17c7b9dd1c18ca22060e1426a82f3ad3ded ./links.html
|
||||
2d344c6b334e66408f7beaa506ec22403c504ed960412c96d870a50173d8d35f3b932079c33bd55a6e2d5ee8093e22c801b6481263ae224073e37d5a636c151b ./links.odt
|
||||
74e5517747befb0b5feaa4c38c6d81576cb2f32d846d2e5d9a720a2cdbbf5273c182ceeb2e056e79906d3a23eafa8a2edd8dd8f80ce115be8ec1fbf7b5c1fdb7 ./links.pdf
|
||||
59909d35b1186f6a84f43a338d6a00a2707133b564fc55d12b5202506ed66583cddbd41f86df84085a1ab688cece919337a3f1a21c4c3f882b60e3fdb8e309d7 ./moderncrypto-rules.html
|
||||
7a27d8d62b1e4ea0e556502e0b5a5b3b681a058ae0f522d55dda1884fa299ab1b5a2a873d9f4f9e804fd67a81ad95849ce888e498d80ac05b326eaf8e30cfac9 ./moderncrypto-rules.odt
|
||||
994ce29f173db65e963aef01ffc98e1c5b74a0c604147bd9b22e76fd300b0a76db5d6a80204fbb3072eed355cba51e793095509c3fad49e0aee6621b53e67103 ./moderncrypto-rules.pdf
|
||||
6ecbea47c219343d2edf71b6c3c0f3017f11939fd0db0d5d712b17d29942f4013a688e6f8b494c5dd545cb51fcc96d39be5a340fb23074f91be20e2f2474247d ./sha256sum.txt
|
||||
49a740a680ae43d50f350a81cab6a7f16e143c425ea637ea6b04741aaf0f32f3cda40b47fc968df63fc593f4fc68b20b323a79c5d3fb3456fdc559e492fa91d1 ./twitter.html
|
||||
dc1809342995d23401eea934cb52642200bea8db1ec7b20c7c5176bd769339a0b416edb3e9fed9c77e4588b946bca6ce72709bb56d6c5d0c6c5956300cd70fc9 ./twitter.odt
|
||||
c138f0b9b5de0cf3f9f1f27019c8b7a966889895f8adff774ea986b3db0bd802022614cb293e5339eb0a4662878239553e20a8337eb80187048a5473def9c7d8 ./twitter.pdf
|
||||
a9b596018d8563a63c8841b16f871bbf3aee6561fd341b79d1337f08a25c593705a3af34c1b662d2201dff840c596232a69864bca638cb8fd426a3f0c095c9ff ./verify.html
|
||||
6822d16a07cde452d921dd0fda00a8749806d985fb3bc8d2b76eb0ddc9831187d5210c48d6e9bc9d55769bd58e17ace2c5307865009437298761acda5835b8ed ./verify.odt
|
||||
7b48f7d8ec77bf49d81aa4893d40cd683d17adb459a551e49846374f207f21f641fc7b5d3eaccbd45fb05e6f7ccd2512b7dd6d14fe9da41afeb90f91a31b0c38 ./verify.pdf
|
||||
@@ -1,7 +0,0 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iHUEABYKAB0WIQSDps+e9XrCW1x/XSkoXmBIoSMhsgUCZKviMQAKCRAoXmBIoSMh
|
||||
svkGAQCNrDz1tQucUPG3lZH6IOkKbiLgDVXfL/h52YcMPsFK4QD+OImotzEDO0tx
|
||||
ZzWHeJDviroGvBdmzkY4eRDY3R0aewg=
|
||||
=5oA8
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -1,4 +0,0 @@
|
||||
untrusted comment: signature from minisign secret key
|
||||
RUSn9xivowlq/hIdHURvk2DszwDWH9ICQ9jLuIuwpY5GJ6Kbzv7S0B8F9Js7KciGq6eFdOw3vTgNDn/nn/PzxCfrg6VuVUef0gI=
|
||||
trusted comment: timestamp:1688986163 file:guide.odt hashed
|
||||
iqace7v6WKbQikCg6kf7+dxPTKH+MtlN5wgT9r7tlTxkw71INMT6bfxuH46J3L1ImGIuqe9GngUnLcMWIJbQDw==
|
||||
@@ -1,7 +0,0 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iHUEABYKAB0WIQSDps+e9XrCW1x/XSkoXmBIoSMhsgUCZKviMwAKCRAoXmBIoSMh
|
||||
sqwyAQCUpk8QviXVTDEC+RKLuTsJFxwTRsKwxl8xzxnuZwsWtgD8CjAxXd2i5RqS
|
||||
E5efpbdSTwZ9ZGmxKlc7SPTD9A6Iigk=
|
||||
=2jH6
|
||||
-----END PGP SIGNATURE-----
|
||||
@@ -1,4 +0,0 @@
|
||||
untrusted comment: signature from minisign secret key
|
||||
RUSn9xivowlq/npQAp+MUSX8aVzJthnJZBD63gEi+AXFxYUz726/k0l8nrpJLOo5OQnWEQe7x0Piy3Ti5pRL8sRDWSTMK5ambg8=
|
||||
trusted comment: timestamp:1688986164 file:guide.pdf hashed
|
||||
ORnsZPbpnO4ff4enexKROJeTrun9B7hBfYFLU17d7Yj3lBwRLf/bpiPzRHnK/NlwTz6bcKWh4qQpkt2+kBMMCQ==
|
||||
-143
@@ -1,143 +0,0 @@
|
||||
---
|
||||
title: "Verify all the Things"
|
||||
description: We are the maintainers of the Hitchhiker's Guide and the PSA Matrix space.
|
||||
schema:
|
||||
"@context": https://schema.org
|
||||
"@type": Organization
|
||||
"@id": https://www.anonymousplanet.org/
|
||||
name: Anonymous Planet
|
||||
url: https://www.anonymousplanet.org/verify/
|
||||
logo: ../media/favicon.png
|
||||
sameAs:
|
||||
- https://github.com/Anon-Planet
|
||||
- https://opencollective.com/anonymousplanetorg
|
||||
- https://mastodon.social/@anonymousplanet
|
||||
---
|
||||
# Verification
|
||||
|
||||
The PDF and ODT files of this guide are cryptographically signed using GPG and [Minisign](https://jedisct1.github.io/minisign). Their integrity can be verified with the published SHA256 Checksum hashes on this website. SHA256 checksums of all the PDF and ODT files are available here in the [sha256sum.txt](./sha256sum.txt) file. SHA256 checksums, signatures, and VirusTotal ("VT") checks of the releases files (containing the whole repository) are available within the latest release information at <https://github.com/Anon-Planet/thgtoa/releases/latest> which will be available as soon as we have a stable release.
|
||||
|
||||
The GPG signatures for each PDF and ODT files are available here:
|
||||
- PDF (Light Theme) Main and Mirrors: [guide.pdf.asc](./guide.pdf.asc)
|
||||
- ODT Main and Mirrors: [guide.odt.asc](./guide.odt.asc)
|
||||
|
||||
The Minisign signatures for each PDF and ODT files are available here:
|
||||
- PDF (Light Theme) Main and Mirrors: [guide.pdf.minisig](./guide.pdf.minisig)
|
||||
- ODT Main and Mirrors: [guide.odt.minisig](./guide.odt.minisig)
|
||||
|
||||
## Using SHA256 checksums
|
||||
|
||||
First get the hash of your local file by following these steps for your OS.
|
||||
|
||||
??? Note "Verify sha256sums on Windows"
|
||||
|
||||
- From a command prompt, run ```certutil -hashfile filename.txt sha256```
|
||||
- Compare the obtained hash result of your local file to the online file's published hash. They should match.
|
||||
|
||||
??? Note "Verify sha256sums on macOS"
|
||||
|
||||
- From a terminal, run ```shasum -a 256 /full/path/to/your/file```
|
||||
- Compare the obtained hash result of your local file to the online file's published hash. They should match.
|
||||
|
||||
??? Note "Verify sha256sums on Linux"
|
||||
|
||||
- From a terminal, run ```sha256sum /full/path/to/your/file```
|
||||
- Compare the obtained hash result of your local file to the online file's published hash. They should match.
|
||||
|
||||
All commits and releases on this repository are cryptographically signed and verified by each collaborator (check for the "Verified" tags on commits and releases).
|
||||
|
||||
## Using GPG
|
||||
|
||||
To verify files with GPG signatures, you should first install gpg on your system.
|
||||
|
||||
??? Note "Verify signatures on Windows"
|
||||
|
||||
- Install gpg4win from <https://www.gpg4win.org/download.html>
|
||||
|
||||
??? Note "Verify signatures on MacOS"
|
||||
|
||||
- Install GPG Tools from <https://gpgtools.org/>
|
||||
|
||||
??? Note "Verify signatures on Linux"
|
||||
|
||||
- `gpg` should be installed by default (if not, use your Linux package manager to install it such as apt (debian) or rpm (red hat))
|
||||
- Import our master signing key from a trusted source of the publisher using the following command from a command prompt or terminal: `gpg --auto-key-locate nodefault,wkd --locate-keys 9EA98278639F1CD853E096CBFF94507587A6A9B9`
|
||||
|
||||
In theory this command should fetch the key from a default keyserver pool. If this doesn't work, you can also download/view it directly, e.g., from [right here](../pgp/anonymousplanet-master.asc).
|
||||
|
||||
As well as the published key on any keyserver below (search for the fingerprint ```9EA98278639F1CD853E096CBFF94507587A6A9B9```):
|
||||
- <https://pgp.mit.edu>
|
||||
- <https://keys.openpgp.org>
|
||||
- <https://keyserver.ubuntu.com>
|
||||
|
||||
You should then import it manually by issuing the following command on any OS:
|
||||
|
||||
```gpg --import 9EA98278639F1CD853E096CBFF94507587A6A9B9.asc```
|
||||
|
||||
The master signing key allows you to verify all other project-related keys. Once you have the master signing key and are confident it's the correct key (nobody has tampered with it), mark the key as trusted by locally signing it:
|
||||
|
||||
```gpg --lsign-key 9EA98278639F1CD853E096CBFF94507587A6A9B9```
|
||||
|
||||
Alternatively, if you use Kleopatra, it will ask you to certify the key. Certify the key to mark it as trusted.
|
||||
|
||||
Once you have the master key downloaded, imported, and certified, you will obtain a copy of the release key.
|
||||
|
||||
```gpg --auto-key-locate nodefault,wkd --locate-keys 83A6CF9EF57AC25B5C7F5D29285E6048A12321B2``` (to import the release signing key)
|
||||
|
||||
<https://anonymousplanet.org/pgp/AnonymousPlanet-Release-Signing-Key_83A6CF9EF57AC25B5C7F5D29285E6048A12321B2.asc> (to download the key yourself)
|
||||
|
||||
If you use GPG directly, you won't need to mark the release signing key as trusted, because it's already signed by the master signing key. If you use Kleopatra, the process to import the release signing key is the same as importing the master signing key.
|
||||
|
||||
Finally, verify the asc signature file (links above) against the PDF file by issuing the following example command:
|
||||
|
||||
```gpg --verify guide.pdf.asc guide.pdf```
|
||||
|
||||
This should output a result showing it matches a signature created by the release signing key, and is therefore a good result.
|
||||
|
||||
## Using Minisign
|
||||
|
||||
To verify the files with Minisign:
|
||||
|
||||
- First, download minisign from <https://jedisct1.github.io/minisign/>.
|
||||
- Download the files along with their \*.minisig signature file (these should be in the same directory).
|
||||
- Download the Minisign public key available on the website and repository: [minisign.pub](minisign.pub) (again, place it in the same directory for convenience).
|
||||
- Run the following command in a command prompt or terminal within the directory with both files: ```minisign -Vm guide.pdf -p minisign.pub```.
|
||||
- Output should show ```Signature and comment signature verified```.
|
||||
|
||||
## Using VirusTotal
|
||||
|
||||
!!! Note "Note about VirusTotal"
|
||||
|
||||
We do not endorse VirusTotal. It should be used with extreme caution, never with any sensitive files, due to their privacy policies. Do not upload sensitive files to VirusTotal.
|
||||
|
||||
The PDF and ODT files of this guide have been automatically scanned by VT, see the links below for an example but do not trust these hashes blindly. Check the hashes match and re-upload to VT if needed:
|
||||
|
||||
??? Note "Verification"
|
||||
|
||||
- PDF file: [[VT Scan]](https://www.virustotal.com/gui/file/8fefe9bc982aa3d89dd1d8f7bc5b89c17b7e5d212826c21c87f2c0795668fac3?nocache=1)
|
||||
- ODT file: [[VT Scan]](https://www.virustotal.com/gui/file/19055de599deecbd9482b4bfba19abb3e44fa9c8b53fefee3d2bd9c587f6ac1e?nocache=1)
|
||||
|
||||
## Manual safety checks
|
||||
|
||||
For additional safety, you can always double check the PDF files using the PDFID tool which you can download at <https://blog.didierstevens.com/programs/pdf-tools/>. (You might be wondering: "Why should I trust a random python script?" Well, it is open-source and well-known. It is also probably a safer bet than trusting a random PDF).
|
||||
|
||||
Here are the steps:
|
||||
|
||||
- Install the latest version (e.g., 3.10.6 stable) of Python, download [pdfid](https://didierstevens.com/files/software/pdfid_v0_2_8.zip) and, from a command prompt or terminal, run:
|
||||
|
||||
```python pdfid.py file-to-check.pdf```
|
||||
|
||||
And you should see the following entries at **0** for safety, this 0 means there is no Javascript or any action that could possibly execute malicious macros, scripts, etc. Normally this won't be necessary as most modern PDF readers won't execute those scripts anyway.
|
||||
|
||||
```
|
||||
/JS 0 #This indicates the presence of Javascript which could be malicious
|
||||
/JavaScript 0 #This indicates the presence of Javascript which could be malicious
|
||||
/AA 0 #This indicates the presence of automatic action on opening
|
||||
/OpenAction 0 #This indicates the presence of automatic action on opening
|
||||
/AcroForm 0 #This indicates the presence of AcroForm which could contain malicious JavaScript
|
||||
/JBIG2Decode 0 #This indicates the PDF uses JBIG2 compression which could be used for obfuscating malicious content
|
||||
/RichMedia 0 #This indicates the presence rich media within the PDF such as Flash
|
||||
/Launch 0 #This counts the launch actions
|
||||
/EmbeddedFile 0 #This indicates there are embedded files within the PDF
|
||||
/XFA 0 #This indicates the presence of XML Forms within the PDF
|
||||
```
|
||||
@@ -1,2 +0,0 @@
|
||||
untrusted comment: minisign public key FE6A09A3AF18F7A7
|
||||
RWSn9xivowlq/ihAzclDBxhCxbYz4bLkC8E645lHgSUlQNlDvoTxO5Fv
|
||||
@@ -1,50 +0,0 @@
|
||||
cd54350f614dd14ee48762fad971a571ddc380fbb15142a92103a9109e7dd5be ./CHANGELOG.html
|
||||
d87af494a9fd97fcdcfcb8018caa082b42df42eb077a5f548fbfe5ce586d1507 ./CHANGELOG.odt
|
||||
b6191622fc4050284715f94cd77e3dec5a04a78841a4c181953303cd1310e708 ./CHANGELOG.pdf
|
||||
548003a4caaa04b54aefce11b4412763f3c871305f6cc9cf591b11eec2862154 ./CODE_OF_CONDUCT.html
|
||||
6d1764f8fb10bed711cf9db83deb5bb06988bf2880c2424d6ccd3931ff4e2968 ./CODE_OF_CONDUCT.odt
|
||||
773bd06bbf3f75b7374db83ddc1c2a5465d6cf8b3663f1919507d42e1f3bcb61 ./CODE_OF_CONDUCT.pdf
|
||||
6607bf670dfeadf98cc993d2a87b4bda3fb7a6ec27ec9111c07b4d7c9a2380b6 ./CONTRIBUTING.html
|
||||
a308c57365963e77070d36f53c4c0f1c64cf5fcd3c6b2b3ca87365386b231d97 ./CONTRIBUTING.odt
|
||||
ac7fed0f8430522859624759d273d389f7fc714fa3a3ca8621765bcfb85880fe ./CONTRIBUTING.pdf
|
||||
70778dc1b6bd10fef2797ae61f1601fd68fb2df3eb2f51857e4b35bb5a9e2afd ./KEY_ROTATION.html
|
||||
42193bd5106c3cae731c8caa2f3ee059ea7f86ead464875fd80fd16e80248015 ./KEY_ROTATION.odt
|
||||
d536fe8196c2ccf1d05beef305f750c257d4e59c7cf37460519323e814e302ca ./KEY_ROTATION.pdf
|
||||
7f88d1005b500c39d5700ec488221578b8f99a1296fc7f1b41715b0fe8e22075 ./LICENSE.html
|
||||
e45a990fdb6bafc4b7fd40e36cadad9223947c9e4c8e73f185d17594cc63321e ./LICENSE.odt
|
||||
f36e9e576e3e7ed903297bd9b7254d895456ebb6ec475bdf67f7165ba1b3791f ./LICENSE.pdf
|
||||
216f990e9a4d708b2fdafbcfd295f08d497c4225079725ece35f5ed1f6df468b ./README.html
|
||||
bd6b9c18a492c01e5e129680db9ddb9e536ddac0f7d3eace560fc8e8ff448847 ./README.odt
|
||||
a2243162e550910f64dd7ce25e2ea2e220b9c3ac6641b23295e3cb5ecd66e16d ./README.pdf
|
||||
4db51e9bde97428385ad80f2812b3dd0fa3b475c87bfb500f2ec194d146a520d ./about.html
|
||||
ce38a0d9fde42b13da965ead822206bb07127618cc4eaafd6a955ee5a4ba9b64 ./about.odt
|
||||
48c5d406faeab2c6c44ce228195deeaa8a7b3d626bc9048b929ef8da00f7453b ./about.pdf
|
||||
ec9d10eb333c418e52254802c1b2a1df2e6f74a4daa917e70e4eb1b735a3cdcd ./briar.html
|
||||
66af9b231cca38a4a3e35788f82404add07f2e3c1fa2d4d4dedf953ebf2b0c57 ./briar.odt
|
||||
1b2277c976962ccc8e1a3e5664650aa930e14236ed0dad880bae8bd25c89d6ed ./briar.pdf
|
||||
596f016ab325f067cb5e76e37813029e00f13dc38f7ad05bfcd541f2d67b2278 ./chatrooms-rules.html
|
||||
71fc15549a3a9da7adaf9972bf008a55fb85f8f56ece77009238a72b0f826ce7 ./chatrooms-rules.odt
|
||||
1661ccf04e15ca7ac5a31a09fe6a9c2bb8505cff03c3bf6ead91dbda2c01067c ./constitution.html
|
||||
c6d7f6b2db6df0a70044ee47d932a1502520e920f8c22093f2a6aea2c7bb859a ./constitution.odt
|
||||
6aea1525e5534b98d058c996a7249896e0afc6036aff7bc5f9f1a94d2e6e4e0e ./constitution.pdf
|
||||
ff77d6856fa40a97076cf9d3fb9bbf335a986f49a784e5a278e1534c037c0452 ./donations.html
|
||||
0b42c1cdaabd1893089e386f6bacf856521ba852fdb2779625ee998b9a1b9323 ./donations.odt
|
||||
332dbaaec2d32b127e886566e12d28c9c1fe74bec7c939d9945aecee30e84ed2 ./donations.pdf
|
||||
e163a4eb53fc327a78d8c00061e983ab8d4533b2b018c9b96df0641aec5769a5 ./guide.html
|
||||
63a6d1d6af9a0324e2414c05dc19fc04b7f3eecbc4d95219283811eb54f3f2df ./guide.odt
|
||||
64e6b3d4efabb5ce748061d9e412037db95263d1b38ee972f327fa03fb6af38c ./guide.pdf
|
||||
7c4338144f1c6b85cee27a78d9eee4d4e03a37a4390e2b6c530314f0d1a5808d ./legacy.html
|
||||
69d81c8d8d31af5ecc8b68e0d66322cb628920431e37389f4b328e1ce0f37276 ./legacy.odt
|
||||
6f1e3d072b5405a5ac75c029873b3447225ea712186ee2649373fb7171a3245a ./legacy.pdf
|
||||
062ff330d5f6066d336b1e199c800b0fcdeb09ffb7a0b8eef824c24fabbc9e7e ./links.html
|
||||
7e1b9cf757e4a5b48f9d0bf73256618565a7bfe226400541038cd712881f24ca ./links.odt
|
||||
71b41176f08023cf93e8742addb9d714f547c3ae1b887cc2a3b0d3c095b2b2bd ./links.pdf
|
||||
dd8a9f1d3ee123f66642a488c4ca423980010b1aa35a3b8feaf88302ddea26b0 ./moderncrypto-rules.html
|
||||
beb73bcc97fb74e928f5eb42ec8b87711743583814c1467370b029082749b819 ./moderncrypto-rules.odt
|
||||
4878c2977e1417b894c69da1df0659ffb73c700062b49bc3af833d73f7b853bd ./moderncrypto-rules.pdf
|
||||
1e081e84477a463f906b801aad55233f07bdf588c84af5cdf19751b128003a34 ./twitter.html
|
||||
4e38e5e0404f6ef4b8b64625a4b4a3c99fa7b19d63584b78fd2e7a2dab37a6c7 ./twitter.odt
|
||||
e917f2aa945d1c29edbba80191978f11a754b6db480fa15a3824f3e2251ef4b5 ./twitter.pdf
|
||||
af017329f529103c534aff02f001b2a16191982659c2b20b44d4b35056512057 ./verify.html
|
||||
57f87f94d97268974df9758630f175011038b7f6ebece30dc0a1eed8cfabb90d ./verify.odt
|
||||
45101d28254ef25bf012c069e8cde23352b117dfd75feb797807fc578fd200af ./verify.pdf
|
||||
Reference in New Issue
Block a user