Skip to content

Latest commit

Β 

History

History
837 lines (645 loc) Β· 25.6 KB

File metadata and controls

837 lines (645 loc) Β· 25.6 KB

πŸ”§ For Developers & Technical Details

This document is for developers who want to understand the technical implementation, contribute to the project, or fork it for their own use.


πŸ“‹ Table of Contents


πŸ—οΈ Architecture Overview

System Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    GitHub Time Capsule System                    β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                   β”‚
β”‚  User Message (Plain Text)                                       β”‚
β”‚         ↓                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  GitHub Actions: seal-the-capsule.yml                     β”‚  β”‚
β”‚  β”‚  Trigger: pull_request [opened]                           β”‚  β”‚
β”‚  β”‚  β”œβ”€ Extract username from PR                              β”‚  β”‚
β”‚  β”‚  β”œβ”€ Validate message file exists                          β”‚  β”‚
β”‚  β”‚  β”œβ”€ Import GPG public key (from GitHub Secrets)           β”‚  β”‚
β”‚  β”‚  β”œβ”€ Encrypt with AES-256-GCM                              β”‚  β”‚
β”‚  β”‚  β”œβ”€ Save encrypted file to /tmp                           β”‚  β”‚
β”‚  β”‚  β”œβ”€ Checkout main branch                                  β”‚  β”‚
β”‚  β”‚  β”œβ”€ Restore encrypted file                                β”‚  β”‚
β”‚  β”‚  β”œβ”€ Commit to main branch                                 β”‚  β”‚
β”‚  β”‚  └─ Close PR with confirmation comment                    β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚         ↓                                                         β”‚
β”‚  Encrypted Message (sealed/*.gpg)                                β”‚
β”‚         ↓                                                         β”‚
β”‚  [WAIT 10 YEARS - Message is locked]                             β”‚
β”‚         ↓                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  GitHub Actions: auto-unveiling.yml                       β”‚  β”‚
β”‚  β”‚  Trigger: schedule (cron: '0 0 1 1 2035')                 β”‚  β”‚
β”‚  β”‚  β”œβ”€ Import GPG private key & passphrase                   β”‚  β”‚
β”‚  β”‚  β”œβ”€ Decrypt all sealed/*.gpg files                        β”‚  β”‚
β”‚  β”‚  β”œβ”€ Save to decrypted-messages/                           β”‚  β”‚
β”‚  β”‚  β”œβ”€ Generate statistics                                   β”‚  β”‚
β”‚  β”‚  β”œβ”€ Create GitHub Release                                 β”‚  β”‚
β”‚  β”‚  β”œβ”€ Publish decryption key publicly                       β”‚  β”‚
β”‚  β”‚  └─ Create announcement issue                             β”‚  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚         ↓                                                         β”‚
β”‚  Decrypted Messages (decrypted-messages/*.txt)                   β”‚
β”‚                                                                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Repository Structure

.
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       β”œβ”€β”€ seal-the-capsule.yml       # Auto-encrypt on PR open
β”‚       └── auto-unveiling.yml         # Auto-decrypt Jan 1, 2035
β”œβ”€β”€ messages/                           # User-submitted messages
β”‚   β”œβ”€β”€ EXAMPLE-MESSAGE.txt
β”‚   └── <username>.txt
β”œβ”€β”€ sealed/                             # Encrypted messages
β”‚   └── <username>.txt.gpg
β”œβ”€β”€ decrypted-messages/                 # Created in 2035
β”‚   └── <username>.txt
β”œβ”€β”€ README.md                           # User guide (non-technical)
β”œβ”€β”€ FOR-DEVELOPERS.md                   # This file
β”œβ”€β”€ LICENSE                             # MIT License
└── .gitignore

πŸ’» Technology Stack

Component Technology Purpose
Encryption GPG 2.x + AES-256-GCM Military-grade message encryption
Key Management RSA 4096-bit Public/private key pair
Automation GitHub Actions 100% automated sealing & unveiling
Storage Git + GitHub Version control & persistence
CI/CD YAML Workflows Automated processing pipeline
Scripting Bash Workflow automation scripts

πŸ” Encryption System

Algorithm Details

  • Cipher: AES-256-GCM (Galois/Counter Mode)
  • Key Type: RSA 4096-bit
  • Tool: GPG (GNU Privacy Guard) 2.x
  • Security Level: Military-grade

Encryption Process (Sealing)

# Import public key from GitHub Secrets
echo "$GPG_PUBLIC_KEY" | gpg --import

# Encrypt message
gpg --encrypt \
    --recipient "GitHub Time Capsule" \
    --armor \
    --trust-model always \
    --cipher-algo AES256 \
    --output "sealed/${USERNAME}.txt.gpg" \
    "messages/${USERNAME}.txt"

# Verify encryption
gpg --list-packets "sealed/${USERNAME}.txt.gpg"

Decryption Process (Unveiling - 2035)

# Import private key and passphrase
echo "$GPG_PRIVATE_KEY" | gpg --batch --import
echo "$GPG_PASSPHRASE" > passphrase.txt

# Decrypt all messages
for encrypted_file in sealed/*.gpg; do
    filename=$(basename "$encrypted_file" .gpg)
    gpg --decrypt \
        --batch \
        --passphrase-file passphrase.txt \
        --output "decrypted-messages/$filename" \
        "$encrypted_file"
    echo "βœ… Decrypted: $filename"
done

# Cleanup
rm passphrase.txt

Key Management

GitHub Secrets (Repository Settings β†’ Secrets):
  GPG_PUBLIC_KEY:      # Public key for encryption (used during sealing)
  GPG_PRIVATE_KEY:     # Private key for decryption (released in 2035)
  GPG_PASSPHRASE:      # Protects the private key

Key Generation:

# Generate 4096-bit RSA key pair
gpg --full-generate-key
# Select: RSA and RSA, 4096 bits, expires: never
# Name: time-capsule-2035
# Email: thanhnguyentuan2007@gmail.com

# Export public key
gpg --armor --export "time-capsule-2035" > public_key.asc

# Export private key
gpg --armor --export-secret-keys "time-capsule-2035" > private_key.asc

# Store in GitHub Secrets (copy entire file contents, including headers)
cat public_key.asc   # Copy this to GPG_PUBLIC_KEY secret
cat private_key.asc  # Copy this to GPG_PRIVATE_KEY secret

βš™οΈ GitHub Actions Workflows

1. Seal the Capsule Workflow

File: .github/workflows/seal-the-capsule.yml
Trigger: pull_request event (type: opened)
Duration: ~15-30 seconds per message

name: πŸ”’ Seal the Capsule

on:
  pull_request:
    types: [opened]

permissions:
  contents: write
  pull-requests: write

jobs:
  seal-message:
    runs-on: ubuntu-latest
    
    steps:
      # 1. Checkout PR branch
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.ref }}
          repository: ${{ github.event.pull_request.head.repo.full_name }}

      # 2. Extract username from PR author
      - name: πŸ“ Extract username
        run: echo "USERNAME=${{ github.event.pull_request.user.login }}" >> $GITHUB_ENV

      # 3. Validate message file exists
      - name: βœ… Validate message file
        run: |
          if [ ! -f "messages/${USERNAME}.txt" ]; then
            echo "❌ Error: messages/${USERNAME}.txt not found"
            exit 1
          fi
          echo "βœ… Message file found: messages/${USERNAME}.txt"

      # 4. Import GPG public key
      - name: πŸ”‘ Import GPG public key
        run: |
          echo "${{ secrets.GPG_PUBLIC_KEY }}" | gpg --import
          gpg --list-keys

      # 5. Encrypt message
      - name: πŸ”’ Encrypt message
        run: |
          mkdir -p sealed
          gpg --encrypt \
              --recipient "GitHub Time Capsule" \
              --armor \
              --trust-model always \
              --output "sealed/${USERNAME}.txt.gpg" \
              "messages/${USERNAME}.txt"
          echo "βœ… Message encrypted: sealed/${USERNAME}.txt.gpg"
          ls -lh "sealed/${USERNAME}.txt.gpg"

      # 6. Save to /tmp (critical: preserves file across checkout)
      - name: πŸ’Ύ Save encrypted file temporarily
        run: |
          cp "sealed/${USERNAME}.txt.gpg" "/tmp/${USERNAME}.txt.gpg"
          echo "βœ… Saved to /tmp for preservation"

      # 7. Checkout main branch
      - uses: actions/checkout@v4
        with:
          ref: main
          token: ${{ secrets.GITHUB_TOKEN }}

      # 8. Restore encrypted file
      - name: πŸ“¦ Restore encrypted file
        run: |
          mkdir -p sealed
          cp "/tmp/${USERNAME}.txt.gpg" "sealed/${USERNAME}.txt.gpg"
          echo "βœ… Restored encrypted file to sealed/"

      # 9. Commit to main branch
      - name: ✍️ Commit encrypted message to main
        run: |
          git config user.name "Time Capsule Bot"
          git config user.email "bot@timecapsule.dev"
          git add "sealed/${USERNAME}.txt.gpg"
          git commit -m "πŸ”’ Sealed message from @${USERNAME}"
          git push origin main
          echo "βœ… Committed and pushed to main"

      # 10. Close PR with confirmation
      - name: πŸŽ‰ Close PR with confirmation
        uses: actions/github-script@v7
        with:
          script: |
            const username = process.env.USERNAME;
            const comment = `## πŸŽ‰ Your Message Has Been Sealed!

**@${username}**, your time capsule message has been successfully encrypted and sealed!

### What Just Happened:
- βœ… Your message was encrypted with AES-256-GCM encryption
- βœ… Saved to: \`sealed/${username}.txt.gpg\`
- βœ… Committed to the main branch
- βœ… This PR is now being closed automatically

### What Happens Next:
- πŸ”’ Your message is now locked until **January 1, 2035**
- ⏰ On that date, it will be automatically decrypted
- πŸŽ‰ You and everyone else can read it then!

### Important Notes:
- πŸ’Ύ Keep a copy of your original message in your fork if you want!
- πŸ” The encrypted version cannot be read by anyone until 2035
- ⭐ Star this repository to remember to come back!

**Thank you for participating in The GitHub Time Capsule!**

---
*πŸ•°οΈ See you on January 1, 2035!*`;

            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
              body: comment
            });

            await github.rest.pulls.update({
              owner: context.repo.owner,
              repo: context.repo.repo,
              pull_number: context.issue.number,
              state: 'closed'
            });

Critical Implementation Detail: File Preservation

The workflow uses /tmp to preserve the encrypted file across the checkout action:

# Problem: Checking out a different branch loses uncommitted files
# Solution: Copy to /tmp before checkout, restore after

# BEFORE checkout - save file
cp "sealed/${USERNAME}.txt.gpg" "/tmp/${USERNAME}.txt.gpg"

# Checkout main branch (would normally lose the file)
git checkout main

# AFTER checkout - restore file
cp "/tmp/${USERNAME}.txt.gpg" "sealed/${USERNAME}.txt.gpg"

This pattern is essential for the workflow to function correctly.


2. Auto-Unveiling Workflow

File: .github/workflows/auto-unveiling.yml
Trigger: Scheduled cron on January 1, 2035 at 00:00 UTC
Duration: Depends on number of messages (~1-5 minutes for 1000 messages)

πŸ“– For End Users: See HOW-TO-READ-2035.md for non-technical instructions on how to access your message in 2035.

name: πŸŽ† Auto-Unveiling 2035

on:
  schedule:
    - cron: '0 0 1 1 2035'  # January 1, 2035, 00:00 UTC
  workflow_dispatch:          # Manual trigger for testing

jobs:
  unveil-capsule:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4

      - name: πŸ”‘ Import GPG private key
        run: |
          echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --batch --import
          echo "${{ secrets.GPG_PASSPHRASE }}" > passphrase.txt
          gpg --list-secret-keys

      - name: πŸ”“ Decrypt all messages
        run: |
          mkdir -p decrypted-messages
          count=0
          
          for encrypted_file in sealed/*.gpg; do
            if [ "$encrypted_file" = "sealed/*.gpg" ]; then
              echo "⚠️  No encrypted files found"
              exit 1
            fi
            
            filename=$(basename "$encrypted_file" .gpg)
            gpg --decrypt \
                --batch \
                --passphrase-file passphrase.txt \
                --output "decrypted-messages/$filename" \
                "$encrypted_file"
            
            ((count++))
            echo "βœ… Decrypted ($count): $filename"
          done
          
          echo "πŸŽ‰ Total messages decrypted: $count"
          echo "TOTAL_MESSAGES=$count" >> $GITHUB_ENV

      - name: πŸ“Š Generate statistics
        run: |
          echo "# πŸŽ† Unveiling Statistics" > STATISTICS.md
          echo "" >> STATISTICS.md
          echo "- **Total Messages:** $TOTAL_MESSAGES" >> STATISTICS.md
          echo "- **Unveiling Date:** $(date)" >> STATISTICS.md
          echo "" >> STATISTICS.md
          echo "## Contributors" >> STATISTICS.md
          ls decrypted-messages/*.txt | xargs -n1 basename | sed 's/.txt//' | sed 's/^/- @/' >> STATISTICS.md

      - name: πŸŽ‰ Create GitHub Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: unveiling-2035
          release_name: πŸŽ† Time Capsule Unveiling - January 1, 2035
          body: |
            # πŸŽ‰ The Time Capsule Has Been Opened!
            
            After 10 years of waiting, all ${{ env.TOTAL_MESSAGES }} messages have been decrypted and revealed!
            
            ## What's Inside:
            - πŸ“‚ **Decrypted Messages:** `decrypted-messages/` folder
            - πŸ”‘ **Decryption Key:** `DECRYPTION_KEY.asc`
            - πŸ“Š **Statistics:** `STATISTICS.md`
            
            ## Thank You
            To everyone who participated in 2025 - thank you for being part of this journey!
            
            πŸ•°οΈ **Welcome to 2035!**

      - name: πŸ“’ Publish decryption key
        run: |
          echo "${{ secrets.GPG_PRIVATE_KEY }}" > DECRYPTION_KEY.asc
          echo "${{ secrets.GPG_PASSPHRASE }}" > PASSPHRASE.txt
          echo "βœ… Decryption key published for verification"

      - name: ✍️ Commit everything
        run: |
          rm passphrase.txt  # Don't commit this one
          git config user.name "Time Capsule Bot"
          git config user.email "bot@timecapsule.dev"
          git add decrypted-messages/ STATISTICS.md DECRYPTION_KEY.asc PASSPHRASE.txt
          git commit -m "πŸŽ† Unveiling: All $TOTAL_MESSAGES messages decrypted!"
          git push origin main

      - name: πŸ“£ Create announcement issue
        uses: actions/github-script@v7
        with:
          script: |
            await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: 'πŸŽ‰ THE TIME CAPSULE HAS BEEN OPENED! (2035 Unveiling)',
              body: `# πŸŽ† Welcome to 2035!

After 10 years of waiting, the GitHub Time Capsule has been automatically opened!

## πŸ“Š Statistics
- **Total Messages:** ${process.env.TOTAL_MESSAGES}
- **Unveiling Date:** ${new Date().toISOString()}

## πŸ“‚ What's Available
- **Decrypted Messages:** Check the \`decrypted-messages/\` folder
- **Release:** See the [Unveiling Release](../releases/tag/unveiling-2035)
- **Statistics:** Read \`STATISTICS.md\`

## πŸŽ‰ Thank You
To everyone who participated back in 2025 - your messages are now part of history!

**Welcome to the future!** πŸš€`
            });

πŸ”’ Security Model

Threat Model

βœ… Protected Against:

  • Unauthorized decryption before 2035 (mathematically impossible without private key)
  • Message tampering (GPG signatures detect modifications)
  • Man-in-the-middle attacks (GitHub's TLS encryption)
  • Brute force attacks (4096-bit RSA β‰ˆ 2^4096 possible keys)
  • Replay attacks (one-time encryption per user)

❌ Not Protected Against:

  • GitHub account compromise (use 2FA!)
  • Quantum computing attacks (post-2030s may break RSA-4096)
  • GitHub platform shutdown (mitigated by backups)
  • Social engineering (phishing, etc.)

Best Practices

  1. Never commit secrets to the repository
  2. Use GitHub Secrets for all sensitive data
  3. Enable 2FA on GitHub accounts
  4. Audit workflow logs regularly
  5. Backup private keys in multiple secure locations
  6. Test workflows in a fork before deploying

πŸ§ͺ Testing & Development

Local Development Setup

# 1. Clone repository
git clone https://github.com/ThanhNguyxn/github-time-capsule.git
cd github-time-capsule

# 2. Install GPG (if not already installed)
# macOS:
brew install gnupg

# Ubuntu/Debian:
sudo apt-get install gnupg

# Windows:
# Download from https://www.gpg4win.org/

# 3. Verify GPG installation
gpg --version

Testing Encryption Locally

# Create test message
echo "Test message from $(date)" > messages/testuser.txt

# Import public key (from GitHub Secrets)
echo "$GPG_PUBLIC_KEY" | gpg --import

# Encrypt
gpg --encrypt \
    --recipient "GitHub Time Capsule" \
    --armor \
    --trust-model always \
    --output sealed/testuser.txt.gpg \
    messages/testuser.txt

# Verify encrypted file
gpg --list-packets sealed/testuser.txt.gpg
ls -lh sealed/testuser.txt.gpg

# Test decryption (requires private key)
gpg --decrypt \
    --batch \
    --passphrase "$GPG_PASSPHRASE" \
    sealed/testuser.txt.gpg

# Compare original and decrypted
diff messages/testuser.txt <(gpg --decrypt --batch --passphrase "$GPG_PASSPHRASE" sealed/testuser.txt.gpg)

Testing via Pull Request

# 1. Create test branch
git checkout -b test-seal-$(date +%s)

# 2. Create test message
echo "Test at $(date)" > messages/$(gh api user -q .login).txt

# 3. Commit and push
git add messages/
git commit -m "Test: Seal workflow"
git push origin HEAD

# 4. Create PR (using GitHub CLI)
gh pr create \
  --title "Test: Seal workflow" \
  --body "Testing automation - please ignore"

# 5. Monitor workflow
gh run watch

# 6. Check workflow status
gh run list --limit 1

# 7. View PR status
gh pr view --json state,comments

# 8. Verify encrypted file was created
gh api repos/:owner/:repo/contents/sealed/$(gh api user -q .login).txt.gpg

# 9. Cleanup
gh pr close
git checkout main
git branch -D test-seal-*

Testing with Act (Local GitHub Actions)

# Install act (https://github.com/nektos/act)
brew install act  # macOS
# or
curl https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash

# Create .secrets file
cat > .secrets << EOF
GPG_PUBLIC_KEY=<your-public-key>
GPG_PRIVATE_KEY=<your-private-key>
GPG_PASSPHRASE=<your-passphrase>
EOF

# Test seal workflow
act pull_request --secret-file .secrets

# Test unveiling workflow
act schedule --secret-file .secrets -j unveil-capsule

🀝 Contributing

How to Contribute

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/your-feature
  3. Make your changes
  4. Test thoroughly (see Testing section above)
  5. Commit with conventional commits: git commit -m "feat: Add feature"
  6. Push: git push origin feature/your-feature
  7. Create Pull Request

Commit Message Format

<type>(<scope>): <subject>

<body>

<footer>

Types:

  • feat: New feature
  • fix: Bug fix
  • docs: Documentation
  • style: Formatting
  • refactor: Code restructuring
  • test: Adding tests
  • chore: Maintenance

Examples:

git commit -m "feat(encryption): Upgrade to AES-256-GCM"
git commit -m "fix(workflow): Preserve encrypted file across checkout"
git commit -m "docs(readme): Add FAQ section"

Areas to Contribute

  • πŸ”’ Security: Review and improve encryption
  • βš™οΈ Workflows: Optimize GitHub Actions
  • πŸ“– Documentation: Improve guides and docs
  • πŸ§ͺ Testing: Add test coverage
  • 🌍 i18n: Add translations
  • 🎨 UI/UX: Improve README design

πŸ“š API Reference

Environment Variables

# GitHub Actions Context
USERNAME:           # Extracted from PR author
GITHUB_TOKEN:       # Auto-provided by GitHub Actions
GITHUB_WORKSPACE:   # Working directory path

# Custom Secrets
GPG_PUBLIC_KEY:     # Public key for encryption
GPG_PRIVATE_KEY:    # Private key for decryption
GPG_PASSPHRASE:     # Passphrase protecting private key

GitHub REST API Endpoints Used

// Create comment on PR
POST /repos/{owner}/{repo}/issues/{issue_number}/comments
Body: { body: "comment text" }

// Close PR
PATCH /repos/{owner}/{repo}/pulls/{pull_number}
Body: { state: "closed" }

// Create release
POST /repos/{owner}/{repo}/releases
Body: { tag_name, name, body }

// Create issue
POST /repos/{owner}/{repo}/issues
Body: { title, body }

GPG Commands Reference

# Key management
gpg --list-keys                    # List public keys
gpg --list-secret-keys             # List private keys
gpg --export <key-id>              # Export public key
gpg --export-secret-keys <key-id>  # Export private key
gpg --import <keyfile>             # Import key
gpg --delete-keys <key-id>         # Delete public key
gpg --delete-secret-keys <key-id>  # Delete private key

# Encryption/Decryption
gpg --encrypt <file>               # Encrypt file
gpg --decrypt <file>               # Decrypt file
gpg --list-packets <file>          # View encrypted file info
gpg --verify <file>                # Verify signature

πŸ› Troubleshooting

Common Issues

Issue: "Message file not found"

Cause: Filename doesn't match GitHub username

Solution:

# Check your exact GitHub username
gh api user -q .login

# Ensure filename matches exactly (case-sensitive)
messages/<exact-username>.txt

Issue: "GPG encryption fails"

Cause: Public key not properly imported

Solution:

# Verify key is imported
gpg --list-keys "GitHub Time Capsule"

# Re-import if needed
echo "$GPG_PUBLIC_KEY" | gpg --import

# Check trust level
gpg --edit-key "GitHub Time Capsule"
> trust
> 5 (ultimate trust)
> quit

Issue: "Encrypted file lost during checkout"

Cause: Not preserved to /tmp before branch switch

Solution: Use the file preservation pattern:

cp "sealed/${USERNAME}.txt.gpg" "/tmp/${USERNAME}.txt.gpg"
git checkout main
cp "/tmp/${USERNAME}.txt.gpg" "sealed/${USERNAME}.txt.gpg"

Issue: "PR doesn't auto-close"

Cause: Insufficient workflow permissions

Solution:

# Add to workflow file
permissions:
  contents: write
  pull-requests: write

Also check: Repository Settings β†’ Actions β†’ General β†’ Workflow permissions β†’ Enable "Read and write permissions"

Issue: "Workflow fails with permission denied"

Cause: GITHUB_TOKEN lacks permissions

Solution:

# Use PAT (Personal Access Token) instead
- uses: actions/checkout@v4
  with:
    token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}

πŸ“– Additional Resources


πŸŽ“ Learning Resources

For Beginners

For Advanced Users


πŸ“ž Support


Built with ❀️ and β˜• by developers, for developers

Back to README | Report Issues | Discussions