Initial commit — Singular Particular Space v1
Homepage (site/index.html): integration-v14 promoted, Writings section integrated with 33 pieces clustered by type (stories/essays/miscellany), Writings welcome lightbox, content frame at 98% opacity. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
27
ToolsnToys/Edge Tab Exporter/manifest.json
Normal file
27
ToolsnToys/Edge Tab Exporter/manifest.json
Normal file
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Edge Tab Catcher",
|
||||
"version": "1.0.0",
|
||||
"description": "Export all open tabs with their URLs, titles, and descriptions to a Markdown file",
|
||||
"permissions": [
|
||||
"tabs",
|
||||
"activeTab",
|
||||
"scripting",
|
||||
"downloads"
|
||||
],
|
||||
"action": {
|
||||
"default_popup": "popup.html",
|
||||
"default_icon": {
|
||||
"16": "icon16.png",
|
||||
"48": "icon48.png",
|
||||
"128": "icon128.png"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "icon16.png",
|
||||
"48": "icon48.png",
|
||||
"128": "icon128.png"
|
||||
},
|
||||
"author": "Your Name",
|
||||
"homepage_url": "https://github.com/yourusername/tab-exporter"
|
||||
}
|
||||
98
ToolsnToys/Edge Tab Exporter/popup.html
Normal file
98
ToolsnToys/Edge Tab Exporter/popup.html
Normal file
@@ -0,0 +1,98 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tab Exporter</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<main class="container" role="main">
|
||||
<header>
|
||||
<h1 id="main-heading">Tab Exporter</h1>
|
||||
<p class="subtitle">Export all your open tabs to a Markdown file</p>
|
||||
</header>
|
||||
|
||||
<section class="stats" aria-labelledby="stats-heading">
|
||||
<h2 id="stats-heading" class="visually-hidden">Tab Statistics</h2>
|
||||
<div class="stat-card" role="status" aria-live="polite">
|
||||
<span class="stat-number" id="tab-count">0</span>
|
||||
<span class="stat-label">Open Tabs</span>
|
||||
</div>
|
||||
<div class="stat-card" role="status" aria-live="polite">
|
||||
<span class="stat-number" id="window-count">0</span>
|
||||
<span class="stat-label">Windows</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="options" aria-labelledby="options-heading">
|
||||
<h2 id="options-heading" class="section-heading">Export Options</h2>
|
||||
|
||||
<div class="option-group">
|
||||
<label class="checkbox-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="include-descriptions"
|
||||
checked
|
||||
aria-describedby="desc-help"
|
||||
>
|
||||
<span>Include page descriptions</span>
|
||||
</label>
|
||||
<p id="desc-help" class="help-text">Fetches meta descriptions from each tab (may take longer)</p>
|
||||
</div>
|
||||
|
||||
<div class="option-group">
|
||||
<label class="checkbox-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="group-by-window"
|
||||
aria-describedby="window-help"
|
||||
>
|
||||
<span>Group tabs by window</span>
|
||||
</label>
|
||||
<p id="window-help" class="help-text">Organizes tabs under separate window headings</p>
|
||||
</div>
|
||||
|
||||
<div class="option-group">
|
||||
<label class="checkbox-label">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="include-timestamp"
|
||||
checked
|
||||
aria-describedby="time-help"
|
||||
>
|
||||
<span>Include export timestamp</span>
|
||||
</label>
|
||||
<p id="time-help" class="help-text">Adds date and time to the exported file</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="actions">
|
||||
<button
|
||||
id="export-btn"
|
||||
class="btn btn-primary"
|
||||
aria-describedby="export-status"
|
||||
>
|
||||
<span class="btn-icon" aria-hidden="true">📥</span>
|
||||
Export Tabs
|
||||
</button>
|
||||
|
||||
<div
|
||||
id="export-status"
|
||||
class="status-message"
|
||||
role="status"
|
||||
aria-live="polite"
|
||||
aria-atomic="true"
|
||||
></div>
|
||||
</section>
|
||||
|
||||
<footer class="footer">
|
||||
<p class="footer-text">
|
||||
<kbd>Ctrl+Click</kbd> a tab to exclude it from export
|
||||
</p>
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
207
ToolsnToys/Edge Tab Exporter/popup.js
Normal file
207
ToolsnToys/Edge Tab Exporter/popup.js
Normal file
@@ -0,0 +1,207 @@
|
||||
// Initialize extension
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
await updateStats();
|
||||
setupEventListeners();
|
||||
});
|
||||
|
||||
// Update tab and window count statistics
|
||||
async function updateStats() {
|
||||
try {
|
||||
const tabs = await chrome.tabs.query({});
|
||||
const windows = await chrome.windows.getAll();
|
||||
|
||||
document.getElementById('tab-count').textContent = tabs.length;
|
||||
document.getElementById('window-count').textContent = windows.length;
|
||||
} catch (error) {
|
||||
console.error('Error updating stats:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup event listeners
|
||||
function setupEventListeners() {
|
||||
const exportBtn = document.getElementById('export-btn');
|
||||
exportBtn.addEventListener('click', handleExport);
|
||||
|
||||
// Keyboard accessibility for options
|
||||
const checkboxes = document.querySelectorAll('input[type="checkbox"]');
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
checkbox.checked = !checkbox.checked;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Main export handler
|
||||
async function handleExport() {
|
||||
const exportBtn = document.getElementById('export-btn');
|
||||
const statusEl = document.getElementById('export-status');
|
||||
|
||||
// Get user options
|
||||
const includeDescriptions = document.getElementById('include-descriptions').checked;
|
||||
const groupByWindow = document.getElementById('group-by-window').checked;
|
||||
const includeTimestamp = document.getElementById('include-timestamp').checked;
|
||||
|
||||
// Disable button and show loading state
|
||||
exportBtn.disabled = true;
|
||||
const originalContent = exportBtn.innerHTML;
|
||||
exportBtn.innerHTML = '<span class="btn-icon loading" aria-hidden="true">⏳</span>Exporting...';
|
||||
|
||||
statusEl.textContent = 'Collecting tab information...';
|
||||
statusEl.className = 'status-message loading';
|
||||
|
||||
try {
|
||||
// Get all tabs
|
||||
const tabs = await chrome.tabs.query({});
|
||||
|
||||
// Fetch descriptions if needed
|
||||
if (includeDescriptions) {
|
||||
statusEl.textContent = `Fetching descriptions (0/${tabs.length})...`;
|
||||
await fetchDescriptions(tabs, statusEl);
|
||||
}
|
||||
|
||||
// Generate markdown content
|
||||
const markdown = await generateMarkdown(tabs, {
|
||||
includeDescriptions,
|
||||
groupByWindow,
|
||||
includeTimestamp
|
||||
});
|
||||
|
||||
// Trigger download
|
||||
downloadMarkdown(markdown);
|
||||
|
||||
// Show success message
|
||||
statusEl.textContent = '✓ Successfully exported ' + tabs.length + ' tabs!';
|
||||
statusEl.className = 'status-message success';
|
||||
|
||||
} catch (error) {
|
||||
console.error('Export error:', error);
|
||||
statusEl.textContent = '✗ Error: ' + error.message;
|
||||
statusEl.className = 'status-message error';
|
||||
} finally {
|
||||
// Re-enable button
|
||||
exportBtn.disabled = false;
|
||||
exportBtn.innerHTML = originalContent;
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch descriptions from tabs
|
||||
async function fetchDescriptions(tabs, statusEl) {
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const tab = tabs[i];
|
||||
statusEl.textContent = `Fetching descriptions (${i + 1}/${tabs.length})...`;
|
||||
|
||||
try {
|
||||
// Try to execute script to get description
|
||||
const results = await chrome.scripting.executeScript({
|
||||
target: { tabId: tab.id },
|
||||
func: extractDescription
|
||||
});
|
||||
|
||||
if (results && results[0] && results[0].result) {
|
||||
tab.description = results[0].result;
|
||||
}
|
||||
} catch (error) {
|
||||
// Tab might not support script injection (e.g., chrome:// pages)
|
||||
tab.description = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to extract description (runs in tab context)
|
||||
function extractDescription() {
|
||||
const metaDesc = document.querySelector('meta[name="description"]');
|
||||
const ogDesc = document.querySelector('meta[property="og:description"]');
|
||||
return (metaDesc && metaDesc.content) ||
|
||||
(ogDesc && ogDesc.content) ||
|
||||
null;
|
||||
}
|
||||
|
||||
// Generate markdown content
|
||||
async function generateMarkdown(tabs, options) {
|
||||
let markdown = '# Browser Tabs Export\n\n';
|
||||
|
||||
if (options.includeTimestamp) {
|
||||
const now = new Date();
|
||||
const timestamp = now.toLocaleString('en-US', {
|
||||
dateStyle: 'full',
|
||||
timeStyle: 'short'
|
||||
});
|
||||
markdown += `**Exported:** ${timestamp}\n\n`;
|
||||
}
|
||||
|
||||
markdown += `**Total Tabs:** ${tabs.length}\n\n`;
|
||||
markdown += '---\n\n';
|
||||
|
||||
if (options.groupByWindow) {
|
||||
// Group tabs by window
|
||||
const windows = await chrome.windows.getAll();
|
||||
const tabsByWindow = {};
|
||||
|
||||
tabs.forEach(tab => {
|
||||
if (!tabsByWindow[tab.windowId]) {
|
||||
tabsByWindow[tab.windowId] = [];
|
||||
}
|
||||
tabsByWindow[tab.windowId].push(tab);
|
||||
});
|
||||
|
||||
let windowIndex = 1;
|
||||
for (const windowId in tabsByWindow) {
|
||||
markdown += `## Window ${windowIndex}\n\n`;
|
||||
markdown += formatTabs(tabsByWindow[windowId], options.includeDescriptions);
|
||||
markdown += '---\n\n';
|
||||
windowIndex++;
|
||||
}
|
||||
} else {
|
||||
// List all tabs together
|
||||
markdown += formatTabs(tabs, options.includeDescriptions);
|
||||
}
|
||||
|
||||
return markdown;
|
||||
}
|
||||
|
||||
// Format tabs as markdown
|
||||
function formatTabs(tabs, includeDescriptions) {
|
||||
let content = '';
|
||||
|
||||
tabs.forEach((tab, index) => {
|
||||
// List item with title and URL
|
||||
content += `- **${escapeMarkdown(tab.title)}**\n`;
|
||||
content += ` - URL: ${tab.url}\n`;
|
||||
|
||||
// Description if available
|
||||
if (includeDescriptions && tab.description) {
|
||||
content += ` - Description: ${escapeMarkdown(tab.description)}\n`;
|
||||
}
|
||||
|
||||
content += '\n';
|
||||
});
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
// Escape markdown special characters
|
||||
function escapeMarkdown(text) {
|
||||
if (!text) return '';
|
||||
return text.replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&');
|
||||
}
|
||||
|
||||
// Download markdown file
|
||||
function downloadMarkdown(content) {
|
||||
const blob = new Blob([content], { type: 'text/markdown;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-');
|
||||
const filename = `tabs-export-${timestamp}.md`;
|
||||
|
||||
chrome.downloads.download({
|
||||
url: url,
|
||||
filename: filename,
|
||||
saveAs: true
|
||||
}, (downloadId) => {
|
||||
// Cleanup object URL after download starts
|
||||
setTimeout(() => URL.revokeObjectURL(url), 100);
|
||||
});
|
||||
}
|
||||
279
ToolsnToys/Edge Tab Exporter/styles.css
Normal file
279
ToolsnToys/Edge Tab Exporter/styles.css
Normal file
@@ -0,0 +1,279 @@
|
||||
:root {
|
||||
--primary-color: #2d8659;
|
||||
--primary-hover: #36a169;
|
||||
--primary-active: #236b47;
|
||||
--secondary-color: #0d9488;
|
||||
--alternate-color: #c4b5fd;
|
||||
--success-color: #2d8659;
|
||||
--error-color: #ef4444;
|
||||
--text-primary: #bfdbfe;
|
||||
--text-secondary: #93c5fd;
|
||||
--bg-primary: linear-gradient(135deg, #1e1b4b 0%, #1e3a8a 100%);
|
||||
--bg-secondary: rgba(255, 255, 255, 0.05);
|
||||
--border-color: rgba(191, 219, 254, 0.2);
|
||||
--shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
|
||||
--shadow-hover: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
--radius: 8px;
|
||||
--transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: var(--text-primary);
|
||||
background: var(--bg-primary);
|
||||
width: 380px;
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
/* Visually hidden but accessible to screen readers */
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 2px solid rgba(196, 181, 253, 0.3);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: var(--alternate-color);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* Stats Section */
|
||||
.stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--radius);
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
transition: var(--transition);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
display: block;
|
||||
font-size: 28px;
|
||||
font-weight: 700;
|
||||
color: var(--secondary-color);
|
||||
line-height: 1;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* Options Section */
|
||||
.options {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.section-heading {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 12px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.option-group {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.checkbox-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 8px;
|
||||
border-radius: 6px;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.checkbox-label:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.checkbox-label input[type="checkbox"] {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
accent-color: var(--primary-color);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.checkbox-label input[type="checkbox"]:focus {
|
||||
outline: 2px solid var(--alternate-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.checkbox-label span {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 4px;
|
||||
margin-left: 28px;
|
||||
}
|
||||
|
||||
/* Actions Section */
|
||||
.actions {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
padding: 14px 20px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
transition: var(--transition);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn:focus {
|
||||
outline: 2px solid var(--alternate-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: var(--primary-color);
|
||||
color: white;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.btn-primary:hover:not(:disabled) {
|
||||
background: var(--primary-hover);
|
||||
box-shadow: var(--shadow-hover);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-primary:active:not(:disabled) {
|
||||
background: var(--primary-active);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.status-message {
|
||||
margin-top: 12px;
|
||||
padding: 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
min-height: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.status-message.success {
|
||||
background: rgba(45, 134, 89, 0.2);
|
||||
color: #6ee7b7;
|
||||
border: 1px solid var(--success-color);
|
||||
}
|
||||
|
||||
.status-message.error {
|
||||
background: rgba(239, 68, 68, 0.2);
|
||||
color: #fca5a5;
|
||||
border: 1px solid var(--error-color);
|
||||
}
|
||||
|
||||
.status-message.loading {
|
||||
background: rgba(13, 148, 136, 0.2);
|
||||
color: var(--secondary-color);
|
||||
border: 1px solid var(--secondary-color);
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.footer {
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid rgba(196, 181, 253, 0.3);
|
||||
}
|
||||
|
||||
.footer-text {
|
||||
font-size: 12px;
|
||||
color: var(--text-secondary);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
kbd {
|
||||
display: inline-block;
|
||||
padding: 2px 6px;
|
||||
font-family: monospace;
|
||||
font-size: 11px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
color: var(--alternate-color);
|
||||
}
|
||||
|
||||
/* Loading spinner animation */
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.btn-icon.loading {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
37
ToolsnToys/Meshagora/.gitignore
vendored
Normal file
37
ToolsnToys/Meshagora/.gitignore
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
|
||||
# Virtual environments
|
||||
venv/
|
||||
.venv/
|
||||
env/
|
||||
.env/
|
||||
|
||||
# Distribution / packaging
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Editor
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Upgrade / internal notes
|
||||
upgrade_notes.md
|
||||
288
ToolsnToys/Meshagora/DEPLOY.md
Normal file
288
ToolsnToys/Meshagora/DEPLOY.md
Normal file
@@ -0,0 +1,288 @@
|
||||
# Meshagora — Deployment & Troubleshooting
|
||||
|
||||
---
|
||||
|
||||
## Version History
|
||||
|
||||
### v0.2 — IP Detection & Startup Transparency
|
||||
- Replaced UDP routing trick with `ifconfig` parsing for hotspot IP detection. The UDP trick failed on the Samsung A25 (and likely other Android builds) because the hotspot interface routing entry may not exist at startup. `ifconfig` reads interface addresses directly via socket ioctls — no netlink, no root, no routing assumptions.
|
||||
- Startup banner now lists all detected non-loopback IPs with interface names, so if the primary guess is wrong the alternatives are immediately visible.
|
||||
- Added `install_termux.sh` — one-shot setup script for Termux.
|
||||
- Added `net-tools` as an explicit dependency (`ifconfig` provider).
|
||||
|
||||
### v0.1 — Initial prototype
|
||||
- Single-file Flask app, in-memory SQLite, runs on Android/Termux hotspot.
|
||||
|
||||
---
|
||||
|
||||
## Dependencies
|
||||
|
||||
### Termux (Android) — required packages
|
||||
|
||||
| Package | Source | Purpose |
|
||||
|---------|--------|---------|
|
||||
| `python` | `pkg` | Runtime |
|
||||
| `net-tools` | `pkg` | `ifconfig` — hotspot IP detection |
|
||||
| `libjpeg-turbo` | `pkg` | JPEG support for Pillow (required by `qrcode[pil]`) |
|
||||
| `libpng` | `pkg` | PNG support for Pillow (required by `qrcode[pil]`) |
|
||||
| `flask` | `pip` | Web framework |
|
||||
| `qrcode[pil]` | `pip` | QR code generation at `/qr` |
|
||||
|
||||
### Desktop (testing only)
|
||||
|
||||
| Package | Source | Purpose |
|
||||
|---------|--------|---------|
|
||||
| `flask` | `pip` | Web framework |
|
||||
| `qrcode[pil]` | `pip` | QR code generation |
|
||||
|
||||
`ifconfig` is standard on desktop Linux/macOS — no extra install needed.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start (Termux)
|
||||
|
||||
**First time only — install dependencies:**
|
||||
```
|
||||
bash install_termux.sh
|
||||
```
|
||||
Or manually:
|
||||
```
|
||||
pkg install python net-tools libjpeg-turbo libpng
|
||||
pip install flask "qrcode[pil]"
|
||||
```
|
||||
|
||||
**Every session:**
|
||||
1. Enable Android mobile hotspot.
|
||||
2. Open Termux and run:
|
||||
```
|
||||
python3 meshagora.py
|
||||
```
|
||||
3. Read the startup banner — note all three keys and the Join URL.
|
||||
4. Open `http://[IP]:5000/qr` on the server device and display it for participants to scan.
|
||||
5. Once everyone has joined, go to `/admin` → HIDE QR.
|
||||
|
||||
---
|
||||
|
||||
## Starting the Server
|
||||
|
||||
1. **Enable the Android mobile hotspot before running the script.**
|
||||
IP detection reads active interfaces at startup — if the hotspot is off, the banner URL will be wrong.
|
||||
|
||||
2. Run:
|
||||
```
|
||||
python3 meshagora.py
|
||||
```
|
||||
|
||||
3. The startup banner prints:
|
||||
```
|
||||
==================================================
|
||||
MESHAGORA v2.3 — SOCIAL SANDBOX
|
||||
Admin Post Key : [4 emoji]
|
||||
Trusted Post Key : [2 emoji]
|
||||
Default Post Key : [2 emoji]
|
||||
--------------------------------------------------
|
||||
Join URL : http://192.168.43.1:5000/join (wlan0)
|
||||
Also try : http://10.0.2.5:5000/join (rmnet0)
|
||||
QR code : http://192.168.43.1:5000/qr
|
||||
If no URL works, run: ifconfig | grep inet
|
||||
==================================================
|
||||
```
|
||||
Write down or photograph all three keys. They cannot be recovered without restarting the server (which destroys the session).
|
||||
|
||||
**Reading the banner:**
|
||||
- "Join URL" is the best-guess hotspot address (`192.168.x.x` preferred).
|
||||
- "Also try" lines show other detected interfaces. The `rmnet` / `10.x.x.x` entries are mobile data — participants on the hotspot WiFi cannot reach those.
|
||||
- Samsung A25 hotspot is typically `192.168.43.1` on `wlan0`.
|
||||
|
||||
4. Navigate to `http://[IP]:5000/qr` on the server device and display the QR code for participants to scan.
|
||||
|
||||
5. Once all participants have joined, hide the QR code from the Admin Panel (`/admin` → HIDE QR).
|
||||
|
||||
---
|
||||
|
||||
## Connecting Participants
|
||||
|
||||
Participants need to:
|
||||
1. Join the hotspot WiFi (name set by the server device's hotspot settings).
|
||||
2. Open a browser and scan the QR code, or type the Join URL from the banner.
|
||||
3. Go to `/join` — choose a username, Profile Key (2 emoji), and Friend Key (2 emoji).
|
||||
|
||||
No app install required. Works on any browser from Android 7.0+, iOS Safari, desktop Chrome/Firefox.
|
||||
|
||||
---
|
||||
|
||||
## Admin Panel
|
||||
|
||||
Navigate to `/admin` from any browser connected to the hotspot.
|
||||
Enter the 4-emoji Admin Post Key when prompted. Authentication persists for that browser session.
|
||||
|
||||
**Actions available:**
|
||||
- Restrict / Unrestrict / Promote / Demote individual users
|
||||
- View the current Default Post Key (large, readable — copy to board or announce verbally)
|
||||
- Rotate Default Key manually
|
||||
- Trigger Flood Attack (creates 5 bot accounts with posts)
|
||||
- Show / Hide the join QR code
|
||||
- View raw database tables (users, posts, votes, reports, keys)
|
||||
- **WHAT THE PLATFORM SEES** — surveillance dashboard (scroll to bottom of admin panel):
|
||||
- *Deanonymization table*: maps every session label (Anon #N) to real username, tier, post count, and net score
|
||||
- *Social graph*: all handshake pairs with CONFIRMED / PENDING status
|
||||
- *Behavioral correlations*: who voted for whom and who reported whom, cross-referenced with confirmed friendships — project mid-session for maximum pedagogical impact
|
||||
|
||||
---
|
||||
|
||||
## Stopping the Server
|
||||
|
||||
`Ctrl+C` in the Termux terminal. The in-memory database is destroyed immediately.
|
||||
There is no persistent storage — every session starts clean.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Server prints `127.0.0.1` instead of the hotspot IP
|
||||
|
||||
**Important:** `127.0.0.1` is the loopback address — it only works in the browser on the server device itself. Participants on other devices will get "connection refused". Flask is still listening on all interfaces (`0.0.0.0`), so the server is reachable — you just need the correct IP.
|
||||
|
||||
**Step 1:** Enable the mobile hotspot *before* running `python3 meshagora.py`, then restart. The hotspot interface only appears after the hotspot is on.
|
||||
|
||||
**Step 2:** If still wrong, check all interfaces manually:
|
||||
```
|
||||
ifconfig | grep inet
|
||||
```
|
||||
Look for an address like `192.168.43.1` under `wlan0` — that's the hotspot gateway. Ignore `127.0.0.1` (loopback) and `10.x.x.x` under `rmnet` (mobile data).
|
||||
|
||||
Note: `ip addr` fails in Termux with "cannot bind netlink socket: permission denied" — always use `ifconfig` instead.
|
||||
|
||||
**Step 3:** Navigate to `http://[correct-ip]:5000/join` directly and distribute that URL to participants.
|
||||
|
||||
**Samsung A-series:** The A25 hotspot is typically `192.168.43.1` on `wlan0`. If the banner's "Join URL" shows a different address, check the "Also try" lines for the `192.168.43.1` entry.
|
||||
|
||||
---
|
||||
|
||||
### `ModuleNotFoundError: No module named 'qrcode'`
|
||||
|
||||
```
|
||||
pip install "qrcode[pil]"
|
||||
```
|
||||
|
||||
If it installs but the error persists:
|
||||
```
|
||||
python3 -m pip install "qrcode[pil]"
|
||||
```
|
||||
|
||||
The server runs without qrcode — `/qr` returns a 404. Participants can type the join URL directly.
|
||||
|
||||
---
|
||||
|
||||
### `ModuleNotFoundError: No module named 'flask'`
|
||||
|
||||
```
|
||||
pip install flask
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `ifconfig` command not found
|
||||
|
||||
```
|
||||
pkg install net-tools
|
||||
```
|
||||
|
||||
This is required for hotspot IP detection in Termux. Without it, the banner will fall back to `127.0.0.1`.
|
||||
|
||||
---
|
||||
|
||||
### Port 5000 already in use
|
||||
|
||||
Common on macOS (AirPlay Receiver uses 5000).
|
||||
|
||||
**Option A:** Kill the other process:
|
||||
```
|
||||
lsof -i :5000
|
||||
kill [PID]
|
||||
```
|
||||
|
||||
**Option B:** Change the port at the bottom of `meshagora.py`:
|
||||
```python
|
||||
app.run(host='0.0.0.0', port=5001, debug=False, threaded=False)
|
||||
```
|
||||
Update the join URL accordingly.
|
||||
|
||||
---
|
||||
|
||||
### Participants can't reach the server
|
||||
|
||||
1. Confirm participants are connected to the *hotspot WiFi*, not their mobile data or a different network.
|
||||
2. Confirm the IP in the banner matches `ifconfig | grep inet` output for the `wlan0` interface.
|
||||
3. Confirm port 5000 is not blocked by a firewall. On Termux this is rarely an issue on a local hotspot.
|
||||
4. Try navigating to the URL from the server device's own browser first to confirm the server is responding.
|
||||
|
||||
---
|
||||
|
||||
### Android kills the server mid-session (Termux background kill)
|
||||
|
||||
Android aggressively kills background processes to save battery.
|
||||
|
||||
**Prevention:**
|
||||
- In Android Settings → Apps → Termux → Battery → set to "Unrestricted" (wording varies by device).
|
||||
- Keep Termux in the foreground with the screen on, or acquire a wakelock:
|
||||
```
|
||||
termux-wake-lock
|
||||
```
|
||||
(Requires the Termux:API app from F-Droid.)
|
||||
- Keep the server device plugged in to power.
|
||||
|
||||
If the server is killed, all session data is lost. Restart with `python3 meshagora.py` — all keys rotate, all users must rejoin.
|
||||
|
||||
---
|
||||
|
||||
### Default Post Key stopped working for everyone
|
||||
|
||||
A user was restricted (by admin action or by 51% community reports). The Default Post Key rotates silently on every restriction. Check the Admin Panel for the new key and distribute it.
|
||||
|
||||
---
|
||||
|
||||
### Emoji picker doesn't appear / looks broken
|
||||
|
||||
The emoji picker is rendered as inline HTML buttons with basic CSS. No JavaScript framework required.
|
||||
|
||||
- If buttons appear but emoji don't render: the device font doesn't support those codepoints. This is rare on Android 7.0+ but possible on stripped-down browsers. Participants can type emoji directly into the visible input field.
|
||||
- If the grid looks misaligned: the scroll container (`max-height: 160px; overflow-y: auto`) is intentional. Scroll within the picker to see all emoji.
|
||||
|
||||
---
|
||||
|
||||
### Post fails with "Post key incorrect." for a RESTRICTED user
|
||||
|
||||
RESTRICTED users require an admin co-sign. The admin must communicate the Admin Post Key directly to the restricted user for them to post. This is by design — it represents supervised posting access.
|
||||
|
||||
---
|
||||
|
||||
### Handshake says "One or both keys are incorrect."
|
||||
|
||||
- Both participants must submit from their own accounts (you cannot complete both sides from one account).
|
||||
- Emoji must match exactly — the picker submits codepoints, not descriptions.
|
||||
- Try clearing both fields and reselecting. The CLEAR button resets the hidden input.
|
||||
- Verify both participants have submitted independently.
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist (Pre-Session)
|
||||
|
||||
- [ ] Hotspot enabled before starting server — correct IP appears in banner
|
||||
- [ ] `/qr` loads a scannable QR code from the server device
|
||||
- [ ] Join as User 1 from a phone — "Anon #1" assigned, Default Post Key shown on join confirmation
|
||||
- [ ] Join as User 2 from a second device or incognito tab — "Anon #2" assigned
|
||||
- [ ] Post from User 1 with correct Default Post Key — appears in feed
|
||||
- [ ] Post from User 1 with wrong key — "Post key incorrect." error
|
||||
- [ ] Vote from User 2 — score updates
|
||||
- [ ] Report from User 2 — spoiler overlay appears, User 1 sees notification banner
|
||||
- [ ] Admin panel accessible at `/admin` with 4-emoji Admin Post Key
|
||||
- [ ] Admin restricts User 1 — Default Post Key rotates (new key visible in admin panel)
|
||||
- [ ] User 1 post fails with old key, succeeds with new key (or admin co-sign)
|
||||
- [ ] Handshake between User 1 and User 2 — confirmed, username appears on posts for the friend
|
||||
- [ ] Friends-only toggle shows only friend posts
|
||||
- [ ] Profile notes edit blocked without Profile Key, succeeds with correct key
|
||||
- [ ] Hide QR via admin → `/qr` returns 404
|
||||
- [ ] Flood Attack → 5 bot posts appear in feed
|
||||
- [ ] Scroll to "WHAT THE PLATFORM SEES" in admin panel — all three panels render (deanonymization, social graph, behavioral correlations)
|
||||
- [ ] Confirm panels are empty (no crash) on a fresh session with no posts/votes/reports
|
||||
674
ToolsnToys/Meshagora/LICENSE
Normal file
674
ToolsnToys/Meshagora/LICENSE
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
102
ToolsnToys/Meshagora/README.md
Normal file
102
ToolsnToys/Meshagora/README.md
Normal file
@@ -0,0 +1,102 @@
|
||||
# Meshagora
|
||||
|
||||
A social platform simulator for teaching digital literacy. Runs as a local web server on an Android phone (via Termux), participants join through their browsers over WiFi — no internet required, no app install, no persistent data.
|
||||
|
||||
Designed for workshop facilitation. Every session starts clean. All data lives in memory and is destroyed when the server stops.
|
||||
|
||||
---
|
||||
|
||||
## What it teaches
|
||||
|
||||
Meshagora recreates the mechanics of real social platforms in a controlled, observable environment:
|
||||
|
||||
- Tiered posting access (who gets amplified, who gets restricted)
|
||||
- Pseudonymity vs. identity (session labels vs. confirmed friends)
|
||||
- Community reporting and auto-restriction
|
||||
- Algorithmic feed ordering (invisible vote weights)
|
||||
- Handshake-based friend networks and friends-only feeds
|
||||
- Flood attacks (bot accounts)
|
||||
- Admin surveillance — the platform sees everything participants don't
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
```
|
||||
Python 3.8+
|
||||
flask
|
||||
qrcode[pil]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start — Termux (Android, recommended for sessions)
|
||||
|
||||
```bash
|
||||
pkg install python git
|
||||
pip install flask qrcode[pil]
|
||||
git clone https://github.com/JL-Kruger/meshagora.git
|
||||
cd meshagora
|
||||
```
|
||||
|
||||
1. Enable the Android **mobile hotspot** before running the script.
|
||||
2. Run:
|
||||
```bash
|
||||
python3 meshagora.py
|
||||
```
|
||||
3. The startup banner prints the join URL, QR code link, and all post keys.
|
||||
4. Participants connect to the hotspot WiFi and open the join URL in any browser.
|
||||
|
||||
See `DEPLOY.md` for the full pre-session checklist, admin panel guide, and troubleshooting.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start — Desktop (testing)
|
||||
|
||||
```bash
|
||||
pip install flask qrcode[pil]
|
||||
python3 meshagora.py
|
||||
```
|
||||
|
||||
Server runs at `http://127.0.0.1:5000`. Open multiple browser tabs or incognito windows to simulate multiple participants.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
Single-file Flask application (`meshagora.py`). No framework, no database file, no external assets. SQLite runs in-memory. `threaded=False` is mandatory — do not change it.
|
||||
|
||||
### Post tiers
|
||||
|
||||
| Tier | Key length | Who has it |
|
||||
|---|---|---|
|
||||
| ADMIN | 4 emoji | Facilitator only |
|
||||
| TRUSTED | 2 emoji | Promoted users |
|
||||
| DEFAULT | 2 emoji | All registered users (rotates on restriction events) |
|
||||
| RESTRICTED | co-sign | Users restricted by admin or community report |
|
||||
|
||||
### Feed ordering
|
||||
|
||||
1. Highest-score post pinned to top
|
||||
2. Middle posts ordered newest-first
|
||||
3. Lowest-score post pinned to bottom
|
||||
|
||||
### Auto-restriction
|
||||
|
||||
When unique reporters exceed 51% of a user's total posts, that user is automatically restricted and the Default Post Key rotates silently.
|
||||
|
||||
---
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `meshagora.py` | Complete implementation |
|
||||
| `meshagora_spec_v2_3.md` | Full design specification |
|
||||
| `DEPLOY.md` | Deployment guide, troubleshooting, pre-session checklist |
|
||||
|
||||
---
|
||||
|
||||
## Stopping the Server
|
||||
|
||||
`Ctrl+C` in the terminal. All session data is gone immediately. This is intentional.
|
||||
871
ToolsnToys/Meshagora/first deployment termux output.txt
Normal file
871
ToolsnToys/Meshagora/first deployment termux output.txt
Normal file
@@ -0,0 +1,871 @@
|
||||
Output from termux on first meshagora test
|
||||
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-wg8pkhvc/overlay/lib/python3.12/site-packages/setuptools/build_meta.py", line 317, in run_setup
|
||||
exec(code, locals())
|
||||
File "<string>", line 1118, in <module>
|
||||
RequiredDependencyException:
|
||||
|
||||
The headers or library files could not be found for jpeg,
|
||||
a required dependency when compiling Pillow from source.
|
||||
|
||||
Please see the install instructions at:
|
||||
https://pillow.readthedocs.io/en/latest/installation/basic-installation.html
|
||||
|
||||
|
||||
[end of output]
|
||||
|
||||
note: This error originates from a subprocess, and is likely not a problem with pip.
|
||||
ERROR: Failed building wheel for pillow
|
||||
Successfully built markupsafe
|
||||
Failed to build pillow
|
||||
error: failed-wheel-build-for-install
|
||||
|
||||
× Failed to build installable wheels for some pyproject.toml based projects
|
||||
╰─> pillow
|
||||
~ $ pkg uodate
|
||||
Unknown command: 'uodate' (run 'pkg help' for usage information)
|
||||
~ $ pkg update
|
||||
No mirror or mirror group selected. You might want to select one by running 'termux-change-repo'
|
||||
Checking availability of current mirror:
|
||||
[] https://linux.domainesia.com/applications/termux/termux-main: ok
|
||||
Hit:1 https://linux.domainesia.com/applications/termux/termux-main stable InRelease
|
||||
Reading package lists… Done
|
||||
Building dependency tree… Done
|
||||
Reading state information… Done
|
||||
72 packages can be upgraded. Run 'apt list —upgradable' to see them.
|
||||
~ $ pkg upgrade
|
||||
No mirror or mirror group selected. You might want to select one by running 'termux-change-repo'
|
||||
Checking availability of current mirror:
|
||||
[ ] https://linux.domainesia.com/applications/termux/termux-main: ok
|
||||
Hit:1 https://linux.domainesia.com/applications/termux/termux-main stable InRelease
|
||||
Reading package lists… Done
|
||||
Building dependency tree… Done
|
||||
Reading state information… Done
|
||||
72 packages can be upgraded. Run 'apt list —upgradable' to see them.
|
||||
Reading package lists… Done
|
||||
Building dependency tree… Done
|
||||
Reading state information… Done
|
||||
Calculating upgrade… Done
|
||||
The following packages will be upgraded:
|
||||
apt bash bzip2 ca-certificates command-not-found
|
||||
coreutils curl dash debianutils dialog diffutils
|
||||
dos2unix dpkg ed findutils gawk gpgv grep gzip
|
||||
inetutils less libandroid-glob libandroid-selinux
|
||||
libandroid-support libassuan libbz2 libc++
|
||||
libcap-ng libcurl libevent libgcrypt libgmp
|
||||
libgnutls libgpg-error libiconv libidn2 liblz4
|
||||
liblzma libmd libmpfr libnettle libnghttp2
|
||||
libnghttp3 libnpth libsmartcols libssh2 libtirpc
|
||||
libunbound libunistring lsof nano net-tools
|
||||
openssl patch pcre2 procps psmisc readline sed
|
||||
tar termux-am termux-am-socket termux-core
|
||||
termux-exec termux-keyring termux-tools unzip
|
||||
util-linux xxhash xz-utils zlib zstd
|
||||
72 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
|
||||
Need to get 19.9 MB of archives.
|
||||
After this operation, 2109 kB of additional disk space will be used.
|
||||
Do you want to continue? [Y/n] y
|
||||
Get:1 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 ca-certificates all 1:2025.11.04 [127 kB]
|
||||
Get:2 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 zlib aarch64 1.3.1-1 [60.1 kB]
|
||||
Get:3 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 openssl aarch64 1:3.6.0 [2482 kB]
|
||||
Get:4 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 pcre2 aarch64 10.47 [961 kB]
|
||||
Get:5 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libandroid-selinux aarch64 14.0.0.11-1 [59.6 kB]
|
||||
Get:6 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libiconv aarch64 1.18-1 [561 kB]
|
||||
Get:7 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libandroid-support aarch64 29-1 [10.9 kB]
|
||||
Get:8 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libc++ aarch64 29 [335 kB]
|
||||
Get:9 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libgmp aarch64 6.3.0-2 [328 kB]
|
||||
Get:10 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 coreutils aarch64 9.9 [778 kB]
|
||||
Get:11 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libbz2 aarch64 1.0.8-8 [26.1 kB]
|
||||
Get:12 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 bzip2 aarch64 1.0.8-8 [26.2 kB]
|
||||
Get:13 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 diffutils aarch64 3.12-2 [163 kB]
|
||||
Get:14 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 gzip aarch64 1.14-1 [87.5 kB]
|
||||
Get:15 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 less aarch64 685 [131 kB]
|
||||
Get:16 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 liblzma aarch64 5.8.1-1 [191 kB]
|
||||
Get:17 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 xz-utils aarch64 5.8.1-1 [71.1 kB]
|
||||
Get:18 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libmd aarch64 1.1.0-1 [40.6 kB]
|
||||
Get:19 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libandroid-glob aarch64 0.6-3 [7032 B]
|
||||
Get:20 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 tar aarch64 1.35-1 [342 kB]
|
||||
Get:21 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 zstd aarch64 1.5.7-1 [360 kB]
|
||||
Get:22 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 dpkg aarch64 1.22.6-5 [308 kB]
|
||||
Get:23 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 findutils aarch64 4.10.0-1 [251 kB]
|
||||
Get:24 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libgpg-error aarch64 1.55-1 [120 kB]
|
||||
Get:25 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libassuan aarch64 3.0.2-1 [73.9 kB]
|
||||
Get:26 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libgcrypt aarch64 1.11.2-1 [500 kB]
|
||||
Get:27 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libnpth aarch64 1.6-3 [11.2 kB]
|
||||
Get:28 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 gpgv aarch64 2.5.11 [187 kB]
|
||||
Get:29 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 grep aarch64 3.12-2 [128 kB]
|
||||
Get:30 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libnettle aarch64 3.10.2-1 [406 kB]
|
||||
Get:31 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libunistring aarch64 1.3-1 [551 kB]
|
||||
Get:32 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libidn2 aarch64 2.3.8-1 [105 kB]
|
||||
Get:33 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libevent aarch64 2.1.12-3 [203 kB]
|
||||
Get:34 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libnghttp2 aarch64 1.68.0 [95.5 kB]
|
||||
Get:35 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libunbound aarch64 1.24.2 [366 kB]
|
||||
Get:36 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libgnutls aarch64 3.8.10 [729 kB]
|
||||
Get:37 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 liblz4 aarch64 1.10.0-1 [84.7 kB]
|
||||
Get:38 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 sed aarch64 4.9-2 [118 kB]
|
||||
Get:39 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libnghttp3 aarch64 1.13.1 [67.4 kB]
|
||||
Get:40 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libssh2 aarch64 1.11.1-1 [218 kB]
|
||||
Get:41 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libcurl aarch64 8.17.0 [990 kB]
|
||||
Get:42 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 curl aarch64 8.17.0 [236 kB]
|
||||
Get:43 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 dash aarch64 0.5.12-1 [65.8 kB]
|
||||
Get:44 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libmpfr aarch64 4.2.1-1 [272 kB]
|
||||
Get:45 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 readline aarch64 8.3.1-2 [294 kB]
|
||||
Get:46 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 gawk aarch64 5.3.1-2 [782 kB]
|
||||
Get:47 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 procps aarch64 3.3.17-6 [143 kB]
|
||||
Get:48 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 psmisc aarch64 23.7-1 [41.2 kB]
|
||||
Get:49 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 termux-am all 0.8.0-2 [577 kB]
|
||||
Get:50 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 termux-am-socket aarch64 1.5.0-1 [16.1 kB]
|
||||
Get:51 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 termux-core aarch64 0.4.0-1 [198 kB]
|
||||
Get:52 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 termux-exec aarch64 1:2.4.0-1 [288 kB]
|
||||
Get:53 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libsmartcols aarch64 2.41.2 [99.8 kB]
|
||||
Get:54 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libcap-ng aarch64 2:0.8.5-1 [35.7 kB]
|
||||
Get:55 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 util-linux aarch64 2.41.2 [738 kB]
|
||||
Get:56 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 dialog aarch64 1.3-20240307-1 [100 kB]
|
||||
Get:57 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 termux-tools aarch64 1.46.0+really1.45.0-1 [33.6 kB]
|
||||
Get:58 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 termux-keyring all 3.13 [39.8 kB]
|
||||
Get:59 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 xxhash aarch64 0.8.3-1 [76.6 kB]
|
||||
Get:60 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 apt aarch64 2.8.1-2 [1031 kB]
|
||||
Get:61 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 bash aarch64 5.3.3-1 [956 kB]
|
||||
Get:62 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 command-not-found aarch64 3.2-6 [106 kB]
|
||||
Get:63 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 debianutils aarch64 5.23.2-1 [16.8 kB]
|
||||
Get:64 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 dos2unix aarch64 7.5.2-1 [64.4 kB]
|
||||
Get:65 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 ed aarch64 1.22.3 [42.8 kB]
|
||||
Get:66 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 inetutils aarch64 2.6-1 [223 kB]
|
||||
Get:67 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 libtirpc aarch64 1.3.7-1 [124 kB]
|
||||
Get:68 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 lsof aarch64 4.99.5-2 [122 kB]
|
||||
Get:69 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 nano aarch64 8.7 [230 kB]
|
||||
Get:70 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 net-tools aarch64 2.10.0-1 [118 kB]
|
||||
Get:71 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 patch aarch64 2.8-1 [74.0 kB]
|
||||
Get:72 https://linux.domainesia.com/applications/termux/termux-main stable/main aarch64 unzip aarch64 6.0-10 [117 kB]
|
||||
Fetched 19.9 MB in 1min 20s (249 kB/s)
|
||||
(Reading database … 17345 files and directories currently installed.)
|
||||
Preparing to unpack …/ca-certificates_1%3a2025.11.04_all.deb …
|
||||
Unpacking ca-certificates (1:2025.11.04) over (1:2025.02.25) …
|
||||
Setting up ca-certificates (1:2025.11.04) …
|
||||
(Reading database … 17345 files and directories currently installed.)
|
||||
Preparing to unpack …/zlib_1.3.1-1_aarch64.deb …
|
||||
Unpacking zlib (1.3.1-1) over (1.3.1) …
|
||||
Setting up zlib (1.3.1-1) …
|
||||
(Reading database … 17345 files and directories currently installed.)
|
||||
Preparing to unpack …/openssl_1%3a3.6.0_aarch64.deb …
|
||||
Unpacking openssl (1:3.6.0) over (1:3.4.1) …
|
||||
Setting up openssl (1:3.6.0) …
|
||||
|
||||
Configuration file '/data/data/com.termux/files/usr/etc/tls/openssl.cnf'
|
||||
⟹ File on system created by you or by a script.
|
||||
⟹ File also in package provided by package maintainer.
|
||||
What would you like to do about it ? Your options are:
|
||||
Y or I : install the package maintainer's version
|
||||
N or O : keep your currently-installed version
|
||||
D : show the differences between the versions
|
||||
Z : start a shell to examine the situation
|
||||
The default action is to keep your current version.
|
||||
*** openssl.cnf (Y/I/N/O/D/Z) [default=N] ? y
|
||||
Installing new version of config file /data/data/com.termux/files/usr/etc/tls/openssl.cnf …
|
||||
(Reading database … 17346 files and directories currently installed.)
|
||||
Preparing to unpack …/pcre2_10.47_aarch64.deb …
|
||||
Unpacking pcre2 (10.47) over (10.45) …
|
||||
Setting up pcre2 (10.47) …
|
||||
(Reading database … 17348 files and directories currently installed.)
|
||||
Preparing to unpack …/libandroid-selinux_14.0.0.11-1_aarch64.deb …
|
||||
Unpacking libandroid-selinux (14.0.0.11-1) over (14.0.0.11) …
|
||||
Setting up libandroid-selinux (14.0.0.11-1) …
|
||||
(Reading database … 17348 files and directories currently installed.)
|
||||
Preparing to unpack …/libiconv_1.18-1_aarch64.deb …
|
||||
Unpacking libiconv (1.18-1) over (1.18) …
|
||||
Setting up libiconv (1.18-1) …
|
||||
(Reading database … 17348 files and directories currently installed.)
|
||||
Preparing to unpack …/libandroid-support_29-1_aarch64.deb …
|
||||
Unpacking libandroid-support (29-1) over (29) …
|
||||
Setting up libandroid-support (29-1) …
|
||||
(Reading database … 17348 files and directories currently installed.)
|
||||
Preparing to unpack …/archives/libc++_29_aarch64.deb …
|
||||
Unpacking libc++ (29) over (27c) …
|
||||
Setting up libc++ (29) …
|
||||
(Reading database … 17348 files and directories currently installed.)
|
||||
Preparing to unpack …/libgmp_6.3.0-2_aarch64.deb …
|
||||
Unpacking libgmp (6.3.0-2) over (6.3.0-1) …
|
||||
Setting up libgmp (6.3.0-2) …
|
||||
(Reading database … 17348 files and directories currently installed.)
|
||||
Preparing to unpack …/coreutils_9.9_aarch64.deb …
|
||||
Unpacking coreutils (9.9) over (9.6-1) …
|
||||
Setting up coreutils (9.9) …
|
||||
update-alternatives: using /data/data/com.termux/files/usr/libexec/coreutils/cat to provide /data/data/com.termux/files/usr/bin/pager (pager) in auto mode
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libbz2_1.0.8-8_aarch64.deb …
|
||||
Unpacking libbz2 (1.0.8-8) over (1.0.8-6) …
|
||||
Setting up libbz2 (1.0.8-8) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/bzip2_1.0.8-8_aarch64.deb …
|
||||
Unpacking bzip2 (1.0.8-8) over (1.0.8-6) …
|
||||
Setting up bzip2 (1.0.8-8) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/diffutils_3.12-2_aarch64.deb …
|
||||
Unpacking diffutils (3.12-2) over (3.11) …
|
||||
Setting up diffutils (3.12-2) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/gzip_1.14-1_aarch64.deb …
|
||||
Unpacking gzip (1.14-1) over (1.13) …
|
||||
Setting up gzip (1.14-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/archives/less_685_aarch64.deb …
|
||||
Unpacking less (685) over (668) …
|
||||
Setting up less (685) …
|
||||
update-alternatives: using /data/data/com.termux/files/usr/bin/less to provide /data/data/com.termux/files/usr/bin/pager (pager) in auto mode
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/liblzma_5.8.1-1_aarch64.deb …
|
||||
Unpacking liblzma (5.8.1-1) over (5.8.0) …
|
||||
Setting up liblzma (5.8.1-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/xz-utils_5.8.1-1_aarch64.deb …
|
||||
Unpacking xz-utils (5.8.1-1) over (5.8.0) …
|
||||
Setting up xz-utils (5.8.1-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libmd_1.1.0-1_aarch64.deb …
|
||||
Unpacking libmd (1.1.0-1) over (1.1.0) …
|
||||
Setting up libmd (1.1.0-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libandroid-glob_0.6-3_aarch64.deb …
|
||||
Unpacking libandroid-glob (0.6-3) over (0.6-2) …
|
||||
Setting up libandroid-glob (0.6-3) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/tar_1.35-1_aarch64.deb …
|
||||
Unpacking tar (1.35-1) over (1.35) …
|
||||
Setting up tar (1.35-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/zstd_1.5.7-1_aarch64.deb …
|
||||
Unpacking zstd (1.5.7-1) over (1.5.7) …
|
||||
Setting up zstd (1.5.7-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/dpkg_1.22.6-5_aarch64.deb …
|
||||
Unpacking dpkg (1.22.6-5) over (1.22.6-1) …
|
||||
Setting up dpkg (1.22.6-5) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/findutils_4.10.0-1_aarch64.deb …
|
||||
Unpacking findutils (4.10.0-1) over (4.10.0) …
|
||||
Setting up findutils (4.10.0-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libgpg-error_1.55-1_aarch64.deb …
|
||||
Unpacking libgpg-error (1.55-1) over (1.50) …
|
||||
Setting up libgpg-error (1.55-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libassuan_3.0.2-1_aarch64.deb …
|
||||
Unpacking libassuan (3.0.2-1) over (3.0.1-2) …
|
||||
Setting up libassuan (3.0.2-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libgcrypt_1.11.2-1_aarch64.deb …
|
||||
Unpacking libgcrypt (1.11.2-1) over (1.11.0) …
|
||||
Setting up libgcrypt (1.11.2-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libnpth_1.6-3_aarch64.deb …
|
||||
Unpacking libnpth (1.6-3) over (1.6-2) …
|
||||
Setting up libnpth (1.6-3) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/gpgv_2.5.11_aarch64.deb …
|
||||
Unpacking gpgv (2.5.11) over (2.4.5-3) …
|
||||
Setting up gpgv (2.5.11) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/grep_3.12-2_aarch64.deb …
|
||||
Unpacking grep (3.12-2) over (3.11) …
|
||||
Setting up grep (3.12-2) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libnettle_3.10.2-1_aarch64.deb …
|
||||
Unpacking libnettle (3.10.2-1) over (3.10.1) …
|
||||
Setting up libnettle (3.10.2-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libunistring_1.3-1_aarch64.deb …
|
||||
Unpacking libunistring (1.3-1) over (1.3) …
|
||||
Setting up libunistring (1.3-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libidn2_2.3.8-1_aarch64.deb …
|
||||
Unpacking libidn2 (2.3.8-1) over (2.3.7) …
|
||||
Setting up libidn2 (2.3.8-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libevent_2.1.12-3_aarch64.deb …
|
||||
Unpacking libevent (2.1.12-3) over (2.1.12-2) …
|
||||
Setting up libevent (2.1.12-3) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libnghttp2_1.68.0_aarch64.deb …
|
||||
Unpacking libnghttp2 (1.68.0) over (1.65.0) …
|
||||
Setting up libnghttp2 (1.68.0) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libunbound_1.24.2_aarch64.deb …
|
||||
Unpacking libunbound (1.24.2) over (1.22.0) …
|
||||
Setting up libunbound (1.24.2) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libgnutls_3.8.10_aarch64.deb …
|
||||
Unpacking libgnutls (3.8.10) over (3.8.9) …
|
||||
Setting up libgnutls (3.8.10) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/liblz4_1.10.0-1_aarch64.deb …
|
||||
Unpacking liblz4 (1.10.0-1) over (1.10.0) …
|
||||
Setting up liblz4 (1.10.0-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/archives/sed_4.9-2_aarch64.deb …
|
||||
Unpacking sed (4.9-2) over (4.9-1) …
|
||||
Setting up sed (4.9-2) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libnghttp3_1.13.1_aarch64.deb …
|
||||
Unpacking libnghttp3 (1.13.1) over (1.8.0) …
|
||||
Setting up libnghttp3 (1.13.1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libssh2_1.11.1-1_aarch64.deb …
|
||||
Unpacking libssh2 (1.11.1-1) over (1.11.1) …
|
||||
Setting up libssh2 (1.11.1-1) …
|
||||
(Reading database … 17349 files and directories currently installed.)
|
||||
Preparing to unpack …/libcurl_8.17.0_aarch64.deb …
|
||||
Unpacking libcurl (8.17.0) over (8.12.1) …
|
||||
Setting up libcurl (8.17.0) …
|
||||
(Reading database … 17365 files and directories currently installed.)
|
||||
Preparing to unpack …/curl_8.17.0_aarch64.deb …
|
||||
Unpacking curl (8.17.0) over (8.12.1) …
|
||||
Setting up curl (8.17.0) …
|
||||
(Reading database … 17365 files and directories currently installed.)
|
||||
Preparing to unpack …/dash_0.5.12-1_aarch64.deb …
|
||||
Unpacking dash (0.5.12-1) over (0.5.12) …
|
||||
Setting up dash (0.5.12-1) …
|
||||
(Reading database … 17365 files and directories currently installed.)
|
||||
Preparing to unpack …/libmpfr_4.2.1-1_aarch64.deb …
|
||||
Unpacking libmpfr (4.2.1-1) over (4.2.1) …
|
||||
Setting up libmpfr (4.2.1-1) …
|
||||
(Reading database … 17365 files and directories currently installed.)
|
||||
Preparing to unpack …/readline_8.3.1-2_aarch64.deb …
|
||||
Unpacking readline (8.3.1-2) over (8.2.13) …
|
||||
Setting up readline (8.3.1-2) …
|
||||
(Reading database … 17370 files and directories currently installed.)
|
||||
Preparing to unpack …/gawk_5.3.1-2_aarch64.deb …
|
||||
Unpacking gawk (5.3.1-2) over (5.3.0) …
|
||||
Setting up gawk (5.3.1-2) …
|
||||
(Reading database … 17368 files and directories currently installed.)
|
||||
Preparing to unpack …/procps_3.3.17-6_aarch64.deb …
|
||||
Unpacking procps (3.3.17-6) over (3.3.17-5) …
|
||||
Setting up procps (3.3.17-6) …
|
||||
(Reading database … 17368 files and directories currently installed.)
|
||||
Preparing to unpack …/psmisc_23.7-1_aarch64.deb …
|
||||
Unpacking psmisc (23.7-1) over (23.7) …
|
||||
Setting up psmisc (23.7-1) …
|
||||
(Reading database … 17368 files and directories currently installed.)
|
||||
Preparing to unpack …/termux-am_0.8.0-2_all.deb …
|
||||
Unpacking termux-am (0.8.0-2) over (0.8.0-1) …
|
||||
Setting up termux-am (0.8.0-2) …
|
||||
(Reading database … 17368 files and directories currently installed.)
|
||||
Preparing to unpack …/termux-am-socket_1.5.0-1_aarch64.deb …
|
||||
Unpacking termux-am-socket (1.5.0-1) over (1.5.0) …
|
||||
Setting up termux-am-socket (1.5.0-1) …
|
||||
(Reading database … 17368 files and directories currently installed.)
|
||||
Preparing to unpack …/termux-core_0.4.0-1_aarch64.deb …
|
||||
Unpacking termux-core (0.4.0-1) over (0.3.0) …
|
||||
Setting up termux-core (0.4.0-1) …
|
||||
(Reading database … 17369 files and directories currently installed.)
|
||||
Preparing to unpack …/termux-exec_1%3a2.4.0-1_aarch64.deb …
|
||||
Unpacking termux-exec (1:2.4.0-1) over (1:2.3.0) …
|
||||
Setting up termux-exec (1:2.4.0-1) …
|
||||
termux-exec.postinst: Start
|
||||
termux-exec.postinst: android_build_version_sdk: '36'
|
||||
termux-exec: Setting primary Termux '$LD_PRELOAD' library in 'libtermux-exec-ld-preload.so' to '/data/data/com.termux/files/usr/lib/libtermux-exec-direct-ld-preload.so'
|
||||
termux-exec.postinst: End
|
||||
(Reading database … 17372 files and directories currently installed.)
|
||||
Preparing to unpack …/libsmartcols_2.41.2_aarch64.deb …
|
||||
Unpacking libsmartcols (2.41.2) over (2.40.2-3) …
|
||||
Setting up libsmartcols (2.41.2) …
|
||||
(Reading database … 17372 files and directories currently installed.)
|
||||
Preparing to unpack …/libcap-ng_2%3a0.8.5-1_aarch64.deb …
|
||||
Unpacking libcap-ng (2:0.8.5-1) over (2:0.8.5) …
|
||||
Setting up libcap-ng (2:0.8.5-1) …
|
||||
(Reading database … 17372 files and directories currently installed.)
|
||||
Preparing to unpack …/util-linux_2.41.2_aarch64.deb …
|
||||
Unpacking util-linux (2.41.2) over (2.40.2-3) …
|
||||
Setting up util-linux (2.41.2) …
|
||||
(Reading database … 17378 files and directories currently installed.)
|
||||
Preparing to unpack …/dialog_1.3-20240307-1_aarch64.deb …
|
||||
Unpacking dialog (1.3-20240307-1) over (1.3-20240307-0) …
|
||||
Setting up dialog (1.3-20240307-1) …
|
||||
(Reading database … 17378 files and directories currently installed.)
|
||||
Preparing to unpack …/termux-tools_1.46.0+really1.45.0-1_aarch64.deb …
|
||||
Unpacking termux-tools (1.46.0+really1.45.0-1) over (1.45.0) …
|
||||
Setting up termux-tools (1.46.0+really1.45.0-1) …
|
||||
(Reading database … 17378 files and directories currently installed.)
|
||||
Preparing to unpack …/termux-keyring_3.13_all.deb …
|
||||
Unpacking termux-keyring (3.13) over (3.12-1) …
|
||||
Setting up termux-keyring (3.13) …
|
||||
(Reading database … 17381 files and directories currently installed.)
|
||||
Preparing to unpack …/xxhash_0.8.3-1_aarch64.deb …
|
||||
Unpacking xxhash (0.8.3-1) over (0.8.3) …
|
||||
Setting up xxhash (0.8.3-1) …
|
||||
(Reading database … 17381 files and directories currently installed.)
|
||||
Preparing to unpack …/apt_2.8.1-2_aarch64.deb …
|
||||
Unpacking apt (2.8.1-2) over (2.8.1-1) …
|
||||
Setting up apt (2.8.1-2) …
|
||||
|
||||
Configuration file '/data/data/com.termux/files/usr/etc/apt/sources.list'
|
||||
⟹ File on system created by you or by a script.
|
||||
⟹ File also in package provided by package maintainer.
|
||||
What would you like to do about it ? Your options are:
|
||||
Y or I : install the package maintainer's version
|
||||
N or O : keep your currently-installed version
|
||||
D : show the differences between the versions
|
||||
Z : start a shell to examine the situation
|
||||
The default action is to keep your current version.
|
||||
*** sources.list (Y/I/N/O/D/Z) [default=N] ? y
|
||||
Installing new version of config file /data/data/com.termux/files/usr/etc/apt/sources.list …
|
||||
(Reading database … 17381 files and directories currently installed.)
|
||||
Preparing to unpack …/bash_5.3.3-1_aarch64.deb …
|
||||
Unpacking bash (5.3.3-1) over (5.2.37-2) …
|
||||
Setting up bash (5.3.3-1) …
|
||||
|
||||
Configuration file '/data/data/com.termux/files/usr/etc/bash.bashrc'
|
||||
⟹ File on system created by you or by a script.
|
||||
⟹ File also in package provided by package maintainer.
|
||||
What would you like to do about it ? Your options are:
|
||||
Y or I : install the package maintainer's version
|
||||
N or O : keep your currently-installed version
|
||||
D : show the differences between the versions
|
||||
Z : start a shell to examine the situation
|
||||
The default action is to keep your current version.
|
||||
*** bash.bashrc (Y/I/N/O/D/Z) [default=N] ? y
|
||||
Installing new version of config file /data/data/com.termux/files/usr/etc/bash.bashrc …
|
||||
(Reading database … 17388 files and directories currently installed.)
|
||||
Preparing to unpack …/00-command-not-found_3.2-6_aarch64.deb …
|
||||
Unpacking command-not-found (3.2-6) over (2.4.0-68) …
|
||||
Preparing to unpack …/01-debianutils_5.23.2-1_aarch64.deb …
|
||||
Unpacking debianutils (5.23.2-1) over (5.21) …
|
||||
Preparing to unpack …/02-dos2unix_7.5.2-1_aarch64.deb …
|
||||
Unpacking dos2unix (7.5.2-1) over (7.5.2) …
|
||||
Preparing to unpack …/03-ed_1.22.3_aarch64.deb …
|
||||
Unpacking ed (1.22.3) over (1.21.1) …
|
||||
Preparing to unpack …/04-inetutils_2.6-1_aarch64.deb …
|
||||
Unpacking inetutils (2.6-1) over (2.6) …
|
||||
Preparing to unpack …/05-libtirpc_1.3.7-1_aarch64.deb …
|
||||
Unpacking libtirpc (1.3.7-1) over (1.3.6) …
|
||||
Preparing to unpack …/06-lsof_4.99.5-2_aarch64.deb …
|
||||
Unpacking lsof (4.99.5-2) over (4.99.4) …
|
||||
Preparing to unpack …/07-nano_8.7_aarch64.deb …
|
||||
Unpacking nano (8.7) over (8.3) …
|
||||
Preparing to unpack …/08-net-tools_2.10.0-1_aarch64.deb …
|
||||
Unpacking net-tools (2.10.0-1) over (2.10.0) …
|
||||
Preparing to unpack …/09-patch_2.8-1_aarch64.deb …
|
||||
Unpacking patch (2.8-1) over (2.7.6-4) …
|
||||
Preparing to unpack …/10-unzip_6.0-10_aarch64.deb …
|
||||
Unpacking unzip (6.0-10) over (6.0-9) …
|
||||
Setting up libtirpc (1.3.7-1) …
|
||||
Setting up net-tools (2.10.0-1) …
|
||||
Setting up inetutils (2.6-1) …
|
||||
Setting up unzip (6.0-10) …
|
||||
Setting up ed (1.22.3) …
|
||||
Setting up command-not-found (3.2-6) …
|
||||
Setting up patch (2.8-1) …
|
||||
Setting up lsof (4.99.5-2) …
|
||||
Setting up nano (8.7) …
|
||||
update-alternatives: updating alternative /data/data/com.termux/files/usr/bin/nano because link group editor has changed slave links
|
||||
update-alternatives: warning: skipping updating manpage database as 'makewhatis' command from 'mandoc' package is not installed
|
||||
Setting up debianutils (5.23.2-1) …
|
||||
Setting up dos2unix (7.5.2-1) …
|
||||
~ $ pkg install libjpeg-turbo libpng freetype libwebppython-dev clang
|
||||
No mirror or mirror group selected. You might want to select one by running 'termux-change-repo'
|
||||
Checking availability of current mirror:
|
||||
[] https://packages-cf.termux.dev/apt/termux-main/: ok
|
||||
Get:1 https://packages-cf.termux.dev/apt/termux-main stable InRelease [14.0 kB]
|
||||
Get:2 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 Packages [550 kB]
|
||||
Fetched 564 kB in 5s (113 kB/s)
|
||||
Reading package lists… Done
|
||||
Building dependency tree… Done
|
||||
Reading state information… Done
|
||||
47 packages can be upgraded. Run 'apt list —upgradable' to see them.
|
||||
Reading package lists… Done
|
||||
Building dependency tree… Done
|
||||
Reading state information… Done
|
||||
E: Unable to locate package libwebppython-dev
|
||||
~ $ pkg install libwebp python-dev
|
||||
No mirror or mirror group selected. You might want to select one by running 'termux-change-repo'
|
||||
Checking availability of current mirror:
|
||||
[ ] https://packages-cf.termux.dev/apt/termux-main/: ok
|
||||
Reading package lists… Done
|
||||
Building dependency tree… Done
|
||||
Reading state information… Done
|
||||
Package python-dev is not available, but is referred to by another package.
|
||||
This may mean that the package is missing, has been obsoleted, or
|
||||
is only available from another source
|
||||
However the following packages replace it:
|
||||
python
|
||||
|
||||
E: Package 'python-dev' has no installation candidate
|
||||
~ $ pip install flask qrcode[pil]
|
||||
Collecting flask
|
||||
Using cached flask-3.1.3-py3-none-any.whl.metadata (3.2 kB)
|
||||
Collecting qrcode[pil]
|
||||
Using cached qrcode-8.2-py3-none-any.whl.metadata (17 kB)
|
||||
Collecting blinker≥1.9.0 (from flask)
|
||||
Using cached blinker-1.9.0-py3-none-any.whl.metadata (1.6 kB)
|
||||
Collecting click≥8.1.3 (from flask)
|
||||
Using cached click-8.3.1-py3-none-any.whl.metadata (2.6 kB)
|
||||
Collecting itsdangerous≥2.2.0 (from flask)
|
||||
Using cached itsdangerous-2.2.0-py3-none-any.whl.metadata (1.9 kB)
|
||||
Collecting jinja2≥3.1.2 (from flask)
|
||||
Using cached jinja2-3.1.6-py3-none-any.whl.metadata (2.9 kB)
|
||||
Collecting markupsafe≥2.1.1 (from flask)
|
||||
Using cached markupsafe-3.0.3-cp312-cp312-linux_aarch64.whl
|
||||
Collecting werkzeug≥3.1.0 (from flask)
|
||||
Using cached werkzeug-3.1.6-py3-none-any.whl.metadata (4.0 kB)
|
||||
Collecting pillow≥9.1.0 (from qrcode[pil])
|
||||
Using cached pillow-12.1.1.tar.gz (47.0 MB)
|
||||
Installing build dependencies … done
|
||||
Getting requirements to build wheel … done
|
||||
Preparing metadata (pyproject.toml) … done
|
||||
Using cached flask-3.1.3-py3-none-any.whl (103 kB)
|
||||
Using cached qrcode-8.2-py3-none-any.whl (45 kB)
|
||||
Using cached blinker-1.9.0-py3-none-any.whl (8.5 kB)
|
||||
Using cached click-8.3.1-py3-none-any.whl (108 kB)
|
||||
Using cached itsdangerous-2.2.0-py3-none-any.whl (16 kB)
|
||||
Using cached jinja2-3.1.6-py3-none-any.whl (134 kB)
|
||||
Using cached werkzeug-3.1.6-py3-none-any.whl (225 kB)
|
||||
Building wheels for collected packages: pillow
|
||||
Building wheel for pillow (pyproject.toml) … error
|
||||
error: subprocess-exited-with-error
|
||||
|
||||
× Building wheel for pillow (pyproject.toml) did not run successfully.
|
||||
│ exit code: 1
|
||||
╰─> [235 lines of output]
|
||||
running bdist_wheel
|
||||
running build
|
||||
running build_py
|
||||
creating build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/AvifImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/BdfFontFile.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/BlpImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/BmpImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/BufrStubImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ContainerIO.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/CurImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/DcxImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/DdsImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/EpsImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ExifTags.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/FitsImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/FliImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/FontFile.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/FpxImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/FtexImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/GbrImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/GdImageFile.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/GifImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/GimpGradientFile.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/GimpPaletteFile.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/GribStubImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/Hdf5StubImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/IcnsImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/IcoImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/Image.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageChops.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageCms.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageColor.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageDraw.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageDraw2.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageEnhance.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageFile.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageFilter.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageFont.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageGrab.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageMath.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageMode.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageMorph.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageOps.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImagePalette.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImagePath.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageQt.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageSequence.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageShow.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageStat.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageText.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageTk.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageTransform.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImageWin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/ImtImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/IptcImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/Jpeg2KImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/JpegImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/JpegPresets.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/McIdasImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/MicImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/MpegImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/MpoImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/MspImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PSDraw.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PaletteFile.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PalmImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PcdImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PcfFontFile.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PcxImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PdfImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PdfParser.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PixarImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/TarIO.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PngImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PpmImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/PsdImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/QoiImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/SgiImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/SpiderImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/SunImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/TgaImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/TiffImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/TiffTags.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/WalImageFile.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/WebPImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/WmfImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/XVThumbImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/XbmImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/XpmImagePlugin.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/__init__.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/__main__.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/_binary.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/_deprecate.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/_tkinter_finder.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/_typing.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/_util.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/_version.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/features.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/report.py → build/lib.linux-aarch64-cpython-312/PIL
|
||||
running egg_info
|
||||
writing src/pillow.egg-info/PKG-INFO
|
||||
writing dependency_links to src/pillow.egg-info/dependency_links.txt
|
||||
writing requirements to src/pillow.egg-info/requires.txt
|
||||
writing top-level names to src/pillow.egg-info/top_level.txt
|
||||
reading manifest file 'src/pillow.egg-info/SOURCES.txt'
|
||||
reading manifest template 'MANIFEST.in'
|
||||
warning: no files found matching '*.c'
|
||||
warning: no files found matching '*.h'
|
||||
warning: no files found matching '*.sh'
|
||||
warning: no files found matching '*.txt'
|
||||
warning: no files found matching '.flake8'
|
||||
warning: no previously-included files found matching '.clang-format'
|
||||
warning: no previously-included files found matching '.coveragerc'
|
||||
warning: no previously-included files found matching '.editorconfig'
|
||||
warning: no previously-included files found matching '.readthedocs.yml'
|
||||
warning: no previously-included files found matching 'codecov.yml'
|
||||
warning: no previously-included files found matching 'renovate.json'
|
||||
warning: no previously-included files found matching 'Tests/images/README.md'
|
||||
warning: no previously-included files found matching 'Tests/images/crash*.tif'
|
||||
warning: no previously-included files found matching 'Tests/images/string_dimension.tiff'
|
||||
warning: no previously-included files matching '.git*' found anywhere in distribution
|
||||
warning: no previously-included files matching '*.so' found anywhere in distribution
|
||||
no previously-included directories found matching '.ci' no previously-included directories found matching 'wheels'
|
||||
no previously-included directories found matching 'winbuild/build'
|
||||
no previously-included directories found matching 'winbuild/depends'
|
||||
no previously-included directories found matching 'Tests/errors' no previously-included directories found matching 'Tests/images/jpeg2000' no previously-included directories found matching 'Tests/images/msp' no previously-included directories found matching 'Tests/images/picins' no previously-included directories found matching 'Tests/images/sunraster' no previously-included directories found matching 'Tests/test-images' adding license file 'LICENSE'
|
||||
writing manifest file 'src/pillow.egg-info/SOURCES.txt'
|
||||
copying src/PIL/_avif.pyi → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/_imaging.pyi → build/lib.linux-aarch64-cpython-312/PIL
|
||||
copying src/PIL/_imagingcms.pyi → build/lib.linux-aarch64-cpython-312/PIL copying src/PIL/_imagingft.pyi → build/lib.linux-aarch64-cpython-312/PIL copying src/PIL/_imagingmath.pyi → build/lib.linux-aarch64-cpython-312/PIL copying src/PIL/_imagingmorph.pyi → build/lib.linux-aarch64-cpython-312/PIL copying src/PIL/_imagingtk.pyi → build/lib.linux-aarch64-cpython-312/PIL copying src/PIL/_webp.pyi → build/lib.linux-aarch64-cpython-312/PIL copying src/PIL/py.typed → build/lib.linux-aarch64-cpython-312/PIL running build_clib
|
||||
building 'pil_imaging_mode' library creating build/temp.linux-aarch64-cpython-312/src/libImaging aarch64-linux-android-clang -fno-strict-overflow -Wsign-compare -Wunreachable-code -DNDEBUG -g -O3 -Wall -fstack-protector-strong -O3 -fstack-protector-strong -O3 -fPIC -c src/libImaging/Mode.c -o build/temp.linux-aarch64-cpython-312/src/libImaging/Mode.o
|
||||
llvm-ar rcs build/temp.linux-aarch64-cpython-312/libpil_imaging_mode.a build/temp.linux-aarch64-cpython-312/src/libImaging/Mode.o running build_ext
|
||||
|
||||
|
||||
The headers or library files could not be found for jpeg,
|
||||
a required dependency when compiling Pillow from source.
|
||||
|
||||
Please see the install instructions at:
|
||||
https://pillow.readthedocs.io/en/latest/installation/basic-installation.html
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 1101, in <module> File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/__init__.py", line 117, in setup
|
||||
return distutils.core.setup(attrs) # type: ignore[return-value]
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/core.py", line 186, in setup
|
||||
return run_commands(dist) ^^^^^^^^^^^^^^^^^^
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/core.py", line 202, in run_commands dist.run_commands()
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/dist.py", line 1000, in run_commands
|
||||
self.run_command(cmd)
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/dist.py", line 1107, in run_command
|
||||
super().run_command(command)
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/dist.py", line 1019, in run_command
|
||||
cmd_obj.run() File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/command/bdist_wheel.py", line 370, in run self.run_command("build")
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/cmd.py", line 341, in run_command
|
||||
self.distribution.run_command(command) File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/dist.py", line 1107, in run_command
|
||||
super().run_command(command)
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/dist.py", line 1019, in run_command cmd_obj.run()
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/command/build.py", line 135, in run
|
||||
self.run_command(cmd_name) File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/cmd.py", line 341, in run_command self.distribution.run_command(command)
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/dist.py", line 1107, in run_command
|
||||
super().run_command(command)
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/dist.py", line 1019, in run_command
|
||||
cmd_obj.run() File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/command/build_ext.py", line 97, in run
|
||||
build ext.run(self) File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/_distutils/command/build_ext.py", line 367, in run self.build_extensions()
|
||||
File "<string>", line 897, in build_extensions
|
||||
RequiredDependencyException: jpeg
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
|
||||
Traceback (most recent call last):
|
||||
File "/data/data/com.termux/files/usr/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 389, in <module>
|
||||
main()
|
||||
File "/data/data/com.termux/files/usr/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 373, in main
|
||||
json_out["return_val"] = hook( hook_input["kwargs"])
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/data/data/com.termux/files/usr/lib/python3.12/site-packages/pip/_vendor/pyproject_hooks/_in_process/_in_process.py", line 280, in build_wheel
|
||||
return build backend().build_wheel(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/data/data/com.termux/files/usr/tmp/pip-install-6uux7pi6/pillow_094bbceb25664f5f96eceee9372926bb/_custom_build/backend.py", line 26, in build_wheel
|
||||
return super().build_wheel(wheel_directory, config_settings, metadata_directory) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/build_meta.py", line 441, in build_wheel return build(['bdist wheel', '—dist-info-dir', str(metadata_directory)])
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/build_meta.py", line 429, in build return self. build_with_temp_dir(
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/build_meta.py", line 410, in build with_temp_dir
|
||||
self.run_setup() File "/data/data/com.termux/files/usr/tmp/pip-install-6uux7pi6/pillow_094bbceb25664f5f96eceee9372926bb/_custom_build/backend.py", line 20, in run_setup return super().run_setup(setup_script) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "/data/data/com.termux/files/usr/tmp/pip-build-env-pjh12tjv/overlay/lib/python3.12/site-packages/setuptools/build_meta.py", line 317, in run_setup
|
||||
exec(code, locals()) File "<string>", line 1118, in <module> RequiredDependencyException:
|
||||
|
||||
The headers or library files could not be found for jpeg,
|
||||
a required dependency when compiling Pillow from source.
|
||||
Please see the install instructions at: https://pillow.readthedocs.io/en/latest/installation/basic-installation.html
|
||||
|
||||
|
||||
[end of output]
|
||||
|
||||
note: This error originates from a subprocess, and is likely not a problem with pip.
|
||||
ERROR: Failed building wheel for pillow Failed to build pillow error: failed-wheel-build-for-install
|
||||
|
||||
× Failed to build installable wheels for some pyproject.toml based projects
|
||||
╰─> pillow ~ $ pkg install libjpeg-turbo libpng python-dev clang pkg-config freetype libwebp
|
||||
No mirror or mirror group selected. You might want to select one by running 'termux-change-repo' Checking availability of current mirror:
|
||||
[] https://packages-cf.termux.dev/apt/termux-main/: ok Reading package lists… Done
|
||||
Building dependency tree… Done Reading state information… Done Package python-dev is not available, but is referred to by another package. This may mean that the package is missing, has been obsoleted, or
|
||||
is only available from another source However the following packages replace it: python
|
||||
|
||||
E: Package 'python-dev' has no installation candidate~ $ pkg install libjpeg-turbo libpng clang pkg-config freetype libwebp No mirror or mirror group selected. You might want to select one by running 'termux-change-repo'
|
||||
Checking availability of current mirror: [ ] https://packages-cf.termux.dev/apt/termux-main/: ok
|
||||
Reading package lists… Done Building dependency tree… Done Reading state information… Done
|
||||
pkg-config is already the newest version (0.29.2-3). pkg-config set to manually installed. The following additional packages will be installed:
|
||||
brotli giflib libllvm libtiff lld llvm The following NEW packages will be installed: brotli freetype giflib libjpeg-turbo libpng
|
||||
libtiff libwebp The following packages will be upgraded: clang libllvm lld llvm
|
||||
4 upgraded, 7 newly installed, 0 to remove and 43 not upgraded. Need to get 81.0 MB of archives.
|
||||
After this operation, 18.6 MB of additional disk space will be used. Do you want to continue? [Y/n] y
|
||||
Get:1 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 brotli aarch64 1.2.0 [341 kB] Get:2 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 llvm aarch64 21.1.8-2 [14.8 MB] Get:3 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 clang aarch64 21.1.8-2 [30.4 MB]
|
||||
38% [3 clang 19.8 MB/30.4 MB 65%] 158 kB/s 4miGet:4 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 lld aarch64 21.1.8-2 [2814 kB]
|
||||
Get:5 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 libllvm aarch64 21.1.8-2 [30.4 MB]
|
||||
Get:6 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 libpng aarch64 1.6.55 [195 kB] Get:7 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 freetype aarch64 2.14.2 [412 kB]
|
||||
Get:8 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 giflib aarch64 5.2.2-1 [18.2 kB]
|
||||
Get:9 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 libjpeg-turbo aarch64 3.1.3 [383 kB] Get:10 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 libtiff aarch64 4.7.1 [848 kB]
|
||||
Get:11 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 libwebp aarch64 1.6.0-rc1-0 [329 kB] Fetched 81.0 MB in 4min 29s (301 kB/s)
|
||||
Selecting previously unselected package brotli. (Reading database … 17388 files and directories currently installed.)
|
||||
Preparing to unpack …/00-brotli_1.2.0_aarch64.deb …
|
||||
Unpacking brotli (1.2.0) …
|
||||
Preparing to unpack …/01-llvm_21.1.8-2_aarch64.deb … Unpacking llvm (21.1.8-2) over (21.1.6) …
|
||||
Preparing to unpack …/02-clang_21.1.8-2_aarch64.deb …
|
||||
Unpacking clang (21.1.8-2) over (21.1.6) … Preparing to unpack …/03-lld_21.1.8-2_aarch64.deb …
|
||||
Unpacking lld (21.1.8-2) over (21.1.6) …
|
||||
Preparing to unpack …/04-libllvm_21.1.8-2_aarch64.deb …
|
||||
Unpacking libllvm (21.1.8-2) over (21.1.6) …
|
||||
Selecting previously unselected package libpng.
|
||||
Preparing to unpack …/05-libpng_1.6.55_aarch64.deb …
|
||||
Unpacking libpng (1.6.55) …
|
||||
Selecting previously unselected package freetype. Preparing to unpack …/06-freetype_2.14.2_aarch64.deb …
|
||||
Unpacking freetype (2.14.2) …
|
||||
Selecting previously unselected package giflib. Preparing to unpack …/07-giflib_5.2.2-1_aarch64.deb …
|
||||
Unpacking giflib (5.2.2-1) …
|
||||
Selecting previously unselected package libjpeg-turbo.
|
||||
Preparing to unpack …/08-libjpeg-turbo_3.1.3_aarch64.deb …
|
||||
Unpacking libjpeg-turbo (3.1.3) … Selecting previously unselected package libtiff.
|
||||
Preparing to unpack …/09-libtiff_4.7.1_aarch64.deb … Unpacking libtiff (4.7.1) …
|
||||
Selecting previously unselected package libwebp. Preparing to unpack …/10-libwebp_1.6.0-rc1-0_aarch64.deb …
|
||||
Unpacking libwebp (1.6.0-rc1-0) …
|
||||
Setting up libpng (1.6.55) … Setting up libjpeg-turbo (3.1.3) …
|
||||
Setting up libllvm (21.1.8-2) …
|
||||
Setting up giflib (5.2.2-1) …
|
||||
Setting up brotli (1.2.0) … Setting up lld (21.1.8-2) …
|
||||
Setting up libtiff (4.7.1) …
|
||||
Setting up freetype (2.14.2) …
|
||||
Setting up llvm (21.1.8-2) … Setting up libwebp (1.6.0-rc1-0) …
|
||||
Setting up clang (21.1.8-2) …
|
||||
~ $ pip install flask qrcode[pil] Collecting flask
|
||||
Using cached flask-3.1.3-py3-none-any.whl.metadata (3.2 kB) Collecting qrcode[pil]
|
||||
Using cached qrcode-8.2-py3-none-any.whl.metadata (17 kB)
|
||||
Collecting blinker≥1.9.0 (from flask) Using cached blinker-1.9.0-py3-none-any.whl.metadata (1.6 kB)
|
||||
Collecting click≥8.1.3 (from flask)
|
||||
Using cached click-8.3.1-py3-none-any.whl.metadata (2.6 kB)
|
||||
Collecting itsdangerous≥2.2.0 (from flask)
|
||||
Using cached itsdangerous-2.2.0-py3-none-any.whl.metadata (1.9 kB) Collecting jinja2≥3.1.2 (from flask)
|
||||
Using cached jinja2-3.1.6-py3-none-any.whl.metadata (2.9 kB)
|
||||
Collecting markupsafe≥2.1.1 (from flask) Using cached markupsafe-3.0.3-cp312-cp312-linux_aarch64.whl
|
||||
Collecting werkzeug≥3.1.0 (from flask) Using cached werkzeug-3.1.6-py3-none-any.whl.metadata (4.0 kB) Collecting pillow≥9.1.0 (from qrcode[pil])
|
||||
Using cached pillow-12.1.1.tar.gz (47.0 MB)
|
||||
Installing build dependencies … done
|
||||
Getting requirements to build wheel … done Preparing metadata (pyproject.toml) … done
|
||||
Using cached flask-3.1.3-py3-none-any.whl (103 kB)
|
||||
Using cached qrcode-8.2-py3-none-any.whl (45 kB)
|
||||
Using cached blinker-1.9.0-py3-none-any.whl (8.5 kB)
|
||||
Using cached click-8.3.1-py3-none-any.whl (108 kB)
|
||||
Using cached itsdangerous-2.2.0-py3-none-any.whl (16 kB)
|
||||
Using cached jinja2-3.1.6-py3-none-any.whl (134 kB)
|
||||
Using cached werkzeug-3.1.6-py3-none-any.whl (225 kB)
|
||||
Building wheels for collected packages: pillow
|
||||
Building wheel for pillow (pyproject.toml) … done
|
||||
Created wheel for pillow: filename=pillow-12.1.1-cp312-cp312-linux_aarch64.whl size=1200433 sha256=bd8fd34796d4b131f670c1e8fcc2de7b7124b824d20f09f26f55dbd903067bdb
|
||||
Stored in directory: /data/data/com.termux/files/home/.cache/pip/wheels/05/f2/ae/60eba7108f4635d4ebefe50da1192219c87f17d63b67cfb9b9
|
||||
Successfully built pillow
|
||||
Installing collected packages: qrcode, pillow, markupsafe, itsdangerous, click, blinker, werkzeug, jinja2, flask
|
||||
Successfully installed blinker-1.9.0 click-8.3.1 flask-3.1.3 itsdangerous-2.2.0 jinja2-3.1.6 markupsafe-3.0.3 pillow-12.1.1 qrcode-8.2 werkzeug-3.1.6
|
||||
~ $ mkdir meshagora_prototype && cd meshagora_prototype
|
||||
~/meshagora_prototype $ git clone https://github.com/JL-Kruger/meshagora.git
|
||||
The program git is not installed. Install it by executing:
|
||||
pkg install git
|
||||
~/meshagora_prototype $ pkg install git No mirror or mirror group selected. You might want to select one by running 'termux-change-repo'
|
||||
Checking availability of current mirror: [] https://packages-cf.termux.dev/apt/termux-main/: ok
|
||||
Reading package lists… Done Building dependency tree… Done
|
||||
Reading state information… Done
|
||||
The following additional packages will be installed: krb5 ldns libdb libedit libresolv-wrapper openssh
|
||||
openssh-sftp-server termux-auth
|
||||
Suggested packages: perl termux-services
|
||||
The following NEW packages will be installed:
|
||||
git krb5 ldns libdb libedit libresolv-wrapper
|
||||
openssh openssh-sftp-server termux-auth
|
||||
0 upgraded, 9 newly installed, 0 to remove and 43 not upgraded.
|
||||
Need to get 7440 kB of archives.
|
||||
After this operation, 45.0 MB of additional disk space will be used.
|
||||
Do you want to continue? [Y/n] y
|
||||
Get:1 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 git aarch64 2.53.0 [4639 kB] Get:2 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 libresolv-wrapper aarch64 1.1.7-6 [11.5 kB]
|
||||
Get:3 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 libdb aarch64 18.1.40-5 [509 kB]
|
||||
Get:4 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 krb5 aarch64 1.22.2 [902 kB]
|
||||
Get:5 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 ldns aarch64 1.8.4-1 [303 kB]
|
||||
Get:6 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 libedit aarch64 20240517-3.1-1 [79.1 kB]
|
||||
Get:7 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 openssh-sftp-server aarch64 10.2p1-1 [55.4 kB]
|
||||
Get:8 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 termux-auth aarch64 1.5.0-1 [6996 B] Get:9 https://packages-cf.termux.dev/apt/termux-main stable/main aarch64 openssh aarch64 10.2p1-1 [934 kB]Fetched 7440 kB in 5s (1551 kB/s)
|
||||
Selecting previously unselected package git. (Reading database … 18003 files and directories currently installed.) Preparing to unpack …/0-git_2.53.0_aarch64.deb …
|
||||
Unpacking git (2.53.0) …
|
||||
Selecting previously unselected package libresolv-wrapper. Preparing to unpack …/1-libresolv-wrapper_1.1.7-6_aarch64.deb …
|
||||
Unpacking libresolv-wrapper (1.1.7-6) …
|
||||
Selecting previously unselected package libdb.
|
||||
Preparing to unpack …/2-libdb_18.1.40-5_aarch64.deb …
|
||||
Unpacking libdb (18.1.40-5) … Selecting previously unselected package krb5.
|
||||
Preparing to unpack …/3-krb5_1.22.2_aarch64.deb …Unpacking krb5 (1.22.2) …
|
||||
Selecting previously unselected package ldns. Preparing to unpack …/4-ldns_1.8.4-1_aarch64.deb … Unpacking ldns (1.8.4-1) …
|
||||
Selecting previously unselected package libedit. Preparing to unpack …/5-libedit_20240517-3.1-1_aarch64.deb … Unpacking libedit (20240517-3.1-1) …
|
||||
Selecting previously unselected package openssh-sftp-server.
|
||||
Preparing to unpack …/6-openssh-sftp-server_10.2p1-1_aarch64.deb …
|
||||
Unpacking openssh-sftp-server (10.2p1-1) … Selecting previously unselected package termux-auth.
|
||||
Preparing to unpack …/7-termux-auth_1.5.0-1_aarch64.deb …
|
||||
Unpacking termux-auth (1.5.0-1) … Selecting previously unselected package openssh.
|
||||
Preparing to unpack …/8-openssh_10.2p1-1_aarch64.deb …
|
||||
Unpacking openssh (10.2p1-1) … Setting up libedit (20240517-3.1-1) …
|
||||
Setting up openssh-sftp-server (10.2p1-1) … Setting up ldns (1.8.4-1) …
|
||||
Setting up git (2.53.0) … Setting up libresolv-wrapper (1.1.7-6) …
|
||||
Setting up termux-auth (1.5.0-1) … Setting up libdb (18.1.40-5) …
|
||||
Setting up krb5 (1.22.2) … Setting up openssh (10.2p1-1) …
|
||||
Generating public/private rsa key pair. Your identification has been saved in /data/data/com.termux/files/usr/etc/ssh/ssh_host_rsa_key Your public key has been saved in /data/data/com.termux/files/usr/etc/ssh/ssh_host_rsa_key.pub The key fingerprint is: If you plan to use the 'ssh-agent'
|
||||
it is recommended to run it as a service. Run 'pkg i termux-services'
|
||||
to install the ('runit') service manager
|
||||
You can enable the ssh-agent service using 'sv-enable ssh-agent'
|
||||
You can also enable sshd to autostart using 'sv-enable sshd'
|
||||
~/meshagora_prototype $ git clone https://github.com/JL-Kruger/meshagora.git
|
||||
Cloning into 'meshagora'… remote: Enumerating objects: 17, done. remote: Counting objects: 100% (17/17), done.
|
||||
remote: Compressing objects: 100% (11/11), done. remote: Total 17 (delta 5), reused 14 (delta 5), pack-reused 0 (from 0) Receiving objects: 100% (17/17), 49.96 KiB | 289.00 KiB/s, done. Resolving deltas: 100% (5/5), done.
|
||||
~/meshagora_prototype $ python3 meshagora.py python3: can't open file '/data/data/com.termux/files/home/meshagora_prototype/meshagora.py': [Errno 2] No such file or directory
|
||||
~/meshagora_prototype $ ls -a . .. meshagora
|
||||
~/meshagora_prototype $ cd meshagora ~/meshagora_prototype/meshagora $ ls -a
|
||||
. .gitignore README.md .. DEPLOY.md meshagora.py
|
||||
.git LICENSE meshagora_spec_v2_3.md ~/meshagora_prototype/meshagora $ python3 meshagora.py ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡==
|
||||
MESHAGORA v2.3 — SOCIAL SANDBOX Join URL : http://127.0.0.1:5000/join
|
||||
Admin Post Key : 😡👻💀🤔 Trusted Post Key : 😀🌈
|
||||
Default Post Key : 🌙😀 ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡==
|
||||
Serving Flask app 'meshagora' Debug mode: off
|
||||
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. Running on all addresses (0.0.0.0)
|
||||
Running on http://127.0.0.1:5000 * Running on http://127.0.0.1:5000
|
||||
Press CTRL+C to quit ~/meshagora_prototype/meshagora
|
||||
26
ToolsnToys/Meshagora/install_termux.sh
Normal file
26
ToolsnToys/Meshagora/install_termux.sh
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/data/data/com.termux/files/usr/bin/bash
|
||||
# Meshagora — Termux dependency installer
|
||||
# Run once before first use: bash install_termux.sh
|
||||
|
||||
set -e
|
||||
|
||||
echo "========================================"
|
||||
echo "Meshagora — Termux Setup"
|
||||
echo "========================================"
|
||||
|
||||
echo ""
|
||||
echo "[1/2] Installing system packages..."
|
||||
pkg install -y python net-tools libjpeg-turbo libpng
|
||||
|
||||
echo ""
|
||||
echo "[2/2] Installing Python packages..."
|
||||
pip install flask "qrcode[pil]"
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo "Setup complete."
|
||||
echo ""
|
||||
echo "Before starting the server:"
|
||||
echo " 1. Enable Android mobile hotspot"
|
||||
echo " 2. Run: python3 meshagora.py"
|
||||
echo "========================================"
|
||||
1415
ToolsnToys/Meshagora/meshagora.py
Normal file
1415
ToolsnToys/Meshagora/meshagora.py
Normal file
File diff suppressed because it is too large
Load Diff
457
ToolsnToys/Meshagora/meshagora_spec_v2_3.md
Normal file
457
ToolsnToys/Meshagora/meshagora_spec_v2_3.md
Normal file
@@ -0,0 +1,457 @@
|
||||
# Meshagora: Social Sandbox Specification (v2.3)
|
||||
|
||||
**Project**: Interactive local-network social platform for teaching digital resilience
|
||||
**Framework**: 3SC — Safety (Caution), Sanity (Courage), Serenity (Competence)
|
||||
**Values**: Trust (the value judgment), Respect (the mechanism), Freedom (the default state)
|
||||
**Target Hardware**: Legacy Android devices (7.0+); iOS via iSH as stretch goal
|
||||
**Deployment Context**: Classrooms and community spaces with unreliable or no internet
|
||||
**Integration Point**: Section 2 of the 3SC Digital Safety Foundations workshop ("The Places It Can Take You")
|
||||
|
||||
---
|
||||
|
||||
## 1. What This Is
|
||||
|
||||
Meshagora is a temporary, local-only social platform that runs on an old phone serving as a WiFi hotspot and web server. Participants connect their devices to the hotspot and interact through a browser-based social feed — posting, taking notes, managing privacy, and exchanging trust tokens.
|
||||
|
||||
It is a micro-fediverse. One server, one community, one session. It teaches the structure of networked social interaction by making participants the administrators of their own social space.
|
||||
|
||||
The system is designed to degrade gracefully into chaos, then be rebuilt by the participants using tools the facilitator provides. The arc of each session moves from freedom through disorder to competence.
|
||||
|
||||
---
|
||||
|
||||
## 2. Pedagogical Design
|
||||
|
||||
### The Arc
|
||||
|
||||
**Phase 1 — The Open Square (Freedom)**
|
||||
Everything is public. Profiles, notes, posts — all visible to all. This is the default state of "the internet before you configure it." The environment is functional but exposed.
|
||||
|
||||
**Phase 2 — The Noise (Loss of Sanity)**
|
||||
The facilitator introduces disruption using the Admin Panel, ideally from a separate device (an old iPad or iPhone — see Section 7). This is announced in advance: *"At some point in this session, the system will be stressed. Your job is to recognize what's happening and build defenses."* This is a fire drill, not an ambush. Disruptions may include: creating sock puppet accounts that flood the feed, rotating the Default Post Key (silently breaking all default-tier users' posting ability), restricting a user (triggering a silent key rotation that affects everyone), showing participants their "private" data in the raw database, or demonstrating that anonymous posts aren't actually anonymous to the server admin.
|
||||
|
||||
**Phase 3 — The Build (Courage + Competence)**
|
||||
All tools — privacy toggles, friend handshakes, friends-only view, report buttons — are available from the start. Participants simply have no motivation to use them until Phase 2 creates the need. Phase 3 is the moment participants start using what was always there. The facilitator may point out features participants haven't noticed ("Did you know you can make your notes private?") but does not unlock anything. The learning is in the transition from "I don't need that" to "I need that now."
|
||||
|
||||
**Phase 4 — The Reflection (Safety)**
|
||||
Discussion: What happened? What did it feel like when the space was chaotic? When your notes were exposed? What changed when you had tools? How does this map to the platforms you actually use?
|
||||
|
||||
### 3SC Mapping
|
||||
|
||||
| Goal | Method | Sandbox Expression |
|
||||
|------|--------|--------------------|
|
||||
| **Safety** | Caution | Inspecting what's public before posting. Using the Profile Key to protect notes. Reporting posts that harm the space. Verifying links and content before trusting them. |
|
||||
| **Sanity** | Courage | Setting boundaries in a noisy space. Choosing to filter rather than flee — or choosing to retreat to friends-only when the square becomes untenable. Confronting the discomfort of exposed data. Deciding whether to add your report to the count. |
|
||||
| **Serenity** | Competence | Understanding *how* the privacy filters work. Knowing that "delete" means something specific in a database. Building the skill to configure your own environment. Recognizing that retreating to friends-only has consequences for the public square you left behind. |
|
||||
|
||||
### Connection to Broader Workshop
|
||||
|
||||
Meshagora gives participants direct experience of concepts introduced elsewhere in the 3SC workshop:
|
||||
|
||||
- **Section 1 (The Thing in Your Pocket)**: The server device demonstrates that phones are computers, not just screens. Participants see a phone *serving* content, not just consuming it.
|
||||
- **Section 2 (The Places It Can Take You)**: Meshagora *is* the social platform. Its structure mirrors the Fediverse model from the ATProto/Fediverse primer — one server, community rules, local moderation. When discussing Mastodon or Bluesky later, participants have a visceral reference point.
|
||||
- **Section 3 (The Creatures You Meet There)**: If a chatbot or automated poster is added in future extensions, participants have already seen how a "feed" works from the inside.
|
||||
|
||||
---
|
||||
|
||||
## 3. Technical Architecture
|
||||
|
||||
### Stack
|
||||
|
||||
| Component | Technology | Rationale |
|
||||
|-----------|------------|-----------|
|
||||
| Host environment | Termux (Android) | Reliable, well-maintained, real Linux userland |
|
||||
| Language | Python 3 | Pre-installed in Termux, lowest barrier |
|
||||
| Web framework | Flask | Single-file deployable, minimal dependencies |
|
||||
| Data layer | SQLite (in-memory or file) | No external database server needed |
|
||||
| Network | Android mobile hotspot | Creates LAN with no ISP dependency |
|
||||
| Client | Any web browser | No app installation required for participants |
|
||||
|
||||
### Deployment
|
||||
|
||||
1. Facilitator installs Termux on the server device (Android 7.0+).
|
||||
2. Facilitator runs `pkg install python` and `pip install flask qrcode[pil]`.
|
||||
3. Facilitator enables mobile hotspot on the server device.
|
||||
4. Facilitator runs the server script. On startup, the script:
|
||||
- Creates a fresh SQLite database (no data persists from prior sessions).
|
||||
- Detects the device's hotspot IP address.
|
||||
- Generates a QR code image pointing to `http://[IP]:5000`.
|
||||
- Serves the QR code at a facilitator-only URL (e.g., `/qr`) that can be shown or hidden. The facilitator displays this on the server device's screen during onboarding, then hides it once all participants have joined.
|
||||
5. Participants join the hotspot WiFi and scan the QR code (or navigate to the displayed URL).
|
||||
|
||||
**Critical UX detail**: Nobody types IP addresses. The QR code is non-negotiable for classroom usability.
|
||||
|
||||
### Session Lifecycle
|
||||
|
||||
- The database is created when the server starts.
|
||||
- The database is destroyed when the server stops.
|
||||
- There is no persistent storage between sessions. This is a feature: every class starts clean.
|
||||
- The facilitator controls the lifecycle by starting (`python app.py`) and stopping (`Ctrl+C`) the Flask process.
|
||||
- Optional: The facilitator can trigger a mid-session database wipe to demonstrate data impermanence.
|
||||
|
||||
---
|
||||
|
||||
## 4. Data Model
|
||||
|
||||
### Users
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| `id` | INTEGER, PK | Auto-increment |
|
||||
| `session_label` | TEXT | System-assigned. Derived from id at creation (e.g., "Anon #1"). Used in public square and admin panel. |
|
||||
| `username` | TEXT | Participant-chosen, case-sensitive, secret. Only visible to confirmed friends. |
|
||||
| `profile_notes` | TEXT | Class notes, personal workspace |
|
||||
| `notes_visible` | BOOLEAN | Default: TRUE (public). Toggled by Profile Key. |
|
||||
| `post_tier` | TEXT | One of: `ADMIN`, `TRUSTED`, `DEFAULT`, `RESTRICTED`. Default: `DEFAULT`. |
|
||||
| `created_at` | TIMESTAMP | Session-relative |
|
||||
|
||||
No email. No password hash. No PII. Identity exists only for the duration of the session.
|
||||
|
||||
Usernames are **pseudonymous by default**. In the public square, posts from non-friends display with an opaque session label (e.g., "Anon #7"). Posts from confirmed friends display the friend's username. This means the public square looks anonymous to newcomers and gradually becomes populated with known identities as friendships form. The only way to learn someone's username is through real-world interaction.
|
||||
|
||||
### Keys (Per-User)
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| `id` | INTEGER, PK | Auto-increment |
|
||||
| `user_id` | FK → Users | Owner |
|
||||
| `key_type` | TEXT | One of: `PROFILE`, `FRIEND` |
|
||||
| `emoji_code` | TEXT | Two-emoji string (e.g., "🍎🐶") |
|
||||
|
||||
Each user has exactly one key of each type. The FRIEND key is personal (not per-relationship).
|
||||
|
||||
### Post Keys (Server-Level)
|
||||
|
||||
Post keys are not per-user. They are server-wide secrets managed by the facilitator.
|
||||
|
||||
| Key | Set At | Changes? | Who Uses It |
|
||||
|-----|--------|----------|-------------|
|
||||
| **Admin Post Key** | Server startup (4-emoji) | Never | Facilitator. Also authenticates `/admin` panel and co-signs restricted user posts. |
|
||||
| **Trusted Post Key** | Server startup (2-emoji) | Never | Participants promoted to TRUSTED tier by the facilitator. |
|
||||
| **Default Post Key** | Server startup (2-emoji) | **Rotates** every time a user is restricted | All DEFAULT-tier participants. |
|
||||
| **Restricted posting** | N/A | N/A | RESTRICTED users can only post when an admin co-signs with the Admin Post Key. |
|
||||
|
||||
**The rotation mechanic is the point.** When a user is restricted — whether by the facilitator or by community reports reaching 51% — the Default Post Key changes for *everyone*. No notification is sent. Participants discover the change when their next post attempt fails with a standard error message. They must come to the facilitator for the new key. The facilitator has a "distribute new Default Post Key" button in the Admin Panel but controls the pace and method of distribution.
|
||||
|
||||
### Handshakes
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| `id` | INTEGER, PK | Auto-increment |
|
||||
| `from_user_id` | FK → Users | The submitter |
|
||||
| `to_user_id` | FK → Users | The target |
|
||||
| `submitted_code` | TEXT | 4-emoji string: submitter's friend key + target's friend key |
|
||||
| `created_at` | TIMESTAMP | When submitted |
|
||||
|
||||
A friendship is confirmed when the Handshakes table contains matching inverse entries: A→B with code F1F2 and B→A with code F2F1, where F1 is A's Friend Key and F2 is B's Friend Key.
|
||||
|
||||
### Posts
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| `id` | INTEGER, PK | Auto-increment |
|
||||
| `author_id` | FK → Users | Who posted |
|
||||
| `content` | TEXT | Post body |
|
||||
| `score` | INTEGER | Default: 0. Sum of weighted votes. |
|
||||
| `created_at` | TIMESTAMP | For feed ordering |
|
||||
|
||||
The `score` field is the net result of all weighted votes, recalculated on each vote (write-time, not read-time). Most-positive score pins to top, most-negative pins to bottom.
|
||||
|
||||
### Votes
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| `id` | INTEGER, PK | Auto-increment |
|
||||
| `post_id` | FK → Posts | The post being voted on |
|
||||
| `user_id` | FK → Users | The voter |
|
||||
| `direction` | INTEGER | +1 (like) or -1 (dislike) |
|
||||
|
||||
One vote per user per post. The vote's *weight* is determined by the voter's tier at the time of voting:
|
||||
|
||||
| Voter Tier | Vote Weight |
|
||||
|------------|-------------|
|
||||
| ADMIN | +1 / -1 |
|
||||
| TRUSTED | +2 / -2 |
|
||||
| DEFAULT | +1 / -1 |
|
||||
| RESTRICTED | 0 (vote is recorded but has no effect) |
|
||||
|
||||
Admin votes are deliberately weighted the same as default. The facilitator's power is structural (key management, user restriction), not social (inflated votes). Trusted users are the only tier with amplified voice — a privilege that's invisible unless the facilitator reveals it.
|
||||
|
||||
Vote weight is locked at time of voting. If a user is promoted from DEFAULT to TRUSTED, their past votes do not retroactively gain weight.
|
||||
|
||||
### Reports
|
||||
|
||||
| Field | Type | Notes |
|
||||
|-------|------|-------|
|
||||
| `id` | INTEGER, PK | Auto-increment |
|
||||
| `post_id` | FK → Posts | The reported post |
|
||||
| `reporter_id` | FK → Users | Who filed the report |
|
||||
| `created_at` | TIMESTAMP | When reported |
|
||||
|
||||
One report per reporter per post. Reports have two escalating effects:
|
||||
|
||||
**Spoiler (per-post, immediate):** A single report on a post wraps it in a spoiler overlay — content hidden until clicked. The spoiler is a visual flag, not a removal. Anyone can click through to read the content. Additional reports on the same post don't change the spoiler — one is enough.
|
||||
|
||||
**Restriction (per-user, threshold):** When unique reporters across all of a user's posts reach 51% of total registered participants (excluding the reported user), the system **auto-restricts** that user. This triggers the Default Post Key rotation.
|
||||
|
||||
**"Active participants"** means total registered users in this session, minus the reported user. Flask doesn't maintain persistent connections, so there is no presence detection. The threshold is based on registration count, not who's currently looking at the screen.
|
||||
|
||||
**Design implications:**
|
||||
|
||||
- **The report button is on each post.** Tapping "report" on a post by Anon #7 reports that specific post and spoilers it. The system tracks unique reporters per user across all their posts for the restriction threshold.
|
||||
- **Reported users are notified that they've been reported, but not by whom.** This creates accountability without enabling retaliation. The notification says something like "One of your posts has been reported" — no specifics about which post or who reported it.
|
||||
- **The facilitator can restrict a user on a single report.** The community needs 51%; the admin needs only their judgment. This asymmetry between democratic and authoritarian moderation is the point.
|
||||
- **Mob rule is not prevented.** Nothing stops a majority from restricting someone for a bad joke, an unpopular opinion, or for fun. Community moderation can be just and it can be tyrannical. The system doesn't distinguish.
|
||||
- **Unrestriction is admin-only.** The community can restrict easily but cannot restore. This asymmetry between "easy to punish, hard to forgive" is real, worth experiencing, and worth discussing.
|
||||
- **Friends can report friends.** Friendship does not grant immunity from accountability.
|
||||
- **Restricted users can still report others.** Losing your voice doesn't strip your ability to flag behaviour. Whether that's fair is a discussion question.
|
||||
|
||||
### Trust Relationships (Linked Palindromic Pair)
|
||||
|
||||
Each user has a personal Friend Key — a 2-emoji combo chosen at profile creation.
|
||||
|
||||
**Handshake flow (user-facing):**
|
||||
1. Select the target's username from a list of all registered usernames in the session.
|
||||
2. Enter your own Friend Key (2 emoji).
|
||||
3. Enter the target's Friend Key (2 emoji).
|
||||
4. The system validates the username exists, concatenates (yours + theirs), and submits.
|
||||
|
||||
**Pseudonymity note:** The handshake screen exposes all registered usernames. Participants can browse the full list but cannot map names to real people in the room or to session labels — that still requires IRL exchange. If someone shouts "who's CoolPenguin42?" in class, the pseudonymity is broken socially, not technically. The system provides the cover; maintaining it requires discipline. This is an operational security lesson, not a bug.
|
||||
|
||||
The target must complete the same flow in reverse. The server validates:
|
||||
- The first pair of the submitted code matches the submitter's stored Friend Key.
|
||||
- The second pair matches the target's stored Friend Key.
|
||||
- A matching inverse entry exists from the other side (target → submitter, with target's key first).
|
||||
|
||||
Example:
|
||||
- A's Friend Key: 🍎🐶 — B's Friend Key: 🌙🔥
|
||||
- A's submission (system-built): 🍎🐶🌙🔥 — B's submission: 🌙🔥🍎🐶
|
||||
|
||||
**Security analysis and pedagogical framing: see Section 5 (Friend Key).**
|
||||
|
||||
---
|
||||
|
||||
## 5. The Three Keys
|
||||
|
||||
### Profile Key 🔑
|
||||
|
||||
- **Purpose**: Ownership. Required to edit your profile notes and toggle note visibility.
|
||||
- **Assigned**: On user creation. The participant chooses their own 2-emoji combo.
|
||||
- **Lesson**: Your identity and your data require a key you control. If you lose it (forget it), you lose access to your own notes. Nobody else can recover it for you.
|
||||
|
||||
### Post Keys 📝 (Tiered, Server-Level)
|
||||
|
||||
Post keys are not personal — they are platform credentials managed by the facilitator. See Section 4 (Post Keys) for the key table and rotation trigger rules.
|
||||
|
||||
- **Admin Post Key** (4-emoji): Known only to the facilitator. Never changes. Used to post, co-sign restricted users' posts, perform admin actions, and authenticate the Admin Panel. The 4-emoji length (rather than 2) makes it harder to guess and provides a future teaching hook for pen-testing exercises.
|
||||
- **Trusted Post Key**: Distributed by the facilitator to participants who earn TRUSTED status. Never changes. A stable credential that rewards demonstrated competence and good faith participation.
|
||||
- **Default Post Key**: Distributed to all participants at the start of the session. **Rotates every time a user is restricted.** When it rotates, no one is notified. Participants discover their key is broken when they try to post and receive a standard error message. They must ask the facilitator for the new key. The facilitator has a "distribute new Default Post Key" button in the Admin Panel but chooses when and how to share it — verbally, on the board, whispered to individuals.
|
||||
- **Restricted Posting**: A RESTRICTED user cannot post independently. They can only post if the facilitator co-signs with the Admin Post Key — meaning an admin must actively approve the post. This is supervised access.
|
||||
|
||||
**Lessons taught by this structure:**
|
||||
- The difference between personal credentials (Profile Key, Friend Key) and platform access (Post Keys). You own one; someone else owns the other.
|
||||
- Hierarchies of access exist on every platform. Some users are verified, some are shadowbanned, some are suspended. Now you've felt what each level is like.
|
||||
- Moderation has externalities. Restricting one user disrupts every default user — and the disruption is silent. You only find out when you try to act.
|
||||
- Stability as privilege. TRUSTED users are unaffected by key rotations. That stability is itself a form of power.
|
||||
- Access recovery is a social process. The new key exists, but you have to ask for it. Who you have to ask, and how they choose to distribute it, shapes the community.
|
||||
|
||||
### Friend Key 🤝
|
||||
|
||||
- **Purpose**: Identity-bound trust. Your personal Friend Key is a 2-emoji combo set at profile creation. See Section 4 (Trust Relationships) for the handshake validation logic and data model.
|
||||
- **Assigned**: Chosen by the participant at profile creation.
|
||||
- **The Exchange Ritual**: To become friends, two participants must meet in person and share two things: their username (secret until this moment) and their Friend Key emoji pair. Each then uses the handshake screen to select the other's username from the registered list, enter both keys, and submit. The system concatenates and validates. The physical exchange is the entire point: trust is built face-to-face, then registered on the network.
|
||||
- **Lesson**: Trust requires mutual action, shared knowledge, *and* verified identity. Knowing someone's secret isn't enough to impersonate them — the system checks who you are, not just what you know.
|
||||
- **Security model**: The handshake is identity-bound. If C learns both A's and B's Friend Keys (eavesdropping, social engineering), C still cannot forge the A↔B connection. The system would build C's submission as [C's key + B's key] — which fails validation because C's key isn't A's key. The only way C can impersonate A is by changing C's own Friend Key to match A's — which requires compromising A's Profile Key. This creates a dependency chain: **Profile Key protects Friend Key protects trust relationships.** All keys are secrets. All are attackable. But knowledge alone isn't sufficient without identity.
|
||||
- **Pseudonymity implication**: Friendship doesn't just unlock private notes — it unlocks *who someone is*. In the public square, non-friends are anonymous. Forming a friendship transforms an opaque session label into a known person. This directly teaches the difference between pseudonymity and identity, and why de-anonymization is a meaningful act of trust.
|
||||
|
||||
---
|
||||
|
||||
## 6. User Interface Constraints
|
||||
|
||||
The UI must work on damaged, old, and small-screen devices. These are hard design requirements, not suggestions.
|
||||
|
||||
**Aesthetic direction**: Post-soviet cyberpunk. Functional, raw, slightly brutalist. Monospace fonts, visible borders, no rounded-corner polish. The interface should feel like it was built in a garage from salvaged parts — because it was. This aesthetic is honest about what the system is (a prototype on old hardware) and appealing to younger participants who respond to the vibe of "we built this ourselves."
|
||||
|
||||
Functional constraints:
|
||||
|
||||
- **Large touch targets**: Minimum 48x48px for all interactive elements.
|
||||
- **High contrast**: Dark text on light background. No subtle grays. No low-contrast placeholder text.
|
||||
- **Minimal text**: Labels are short. Instructions use icons + words. The emoji-key input is the primary interaction, not typing.
|
||||
- **No JavaScript frameworks**: The UI is server-rendered HTML with minimal inline JS. This keeps page weight low and avoids compatibility issues on old WebViews.
|
||||
- **Works on cracked screens**: No interactions that require precision tapping in corners. Primary actions are centered.
|
||||
- **Mobile-first**: The UI assumes a phone screen. Desktop is not a design target.
|
||||
|
||||
### Key Screens
|
||||
|
||||
1. **Join**: Choose a username (secret — explain this clearly) → choose Profile Key and Friend Key → receive the current Default Post Key. The system assigns a session label (e.g., "Anon #7") for public display.
|
||||
2. **Square**: The public feed. Highest-scoring post pinned at top. Lowest-scoring post pinned at bottom. Both always visible (a toy recommendation algorithm — "this is what 'trending' means"). Remaining posts newest-first between the pinned extremes. Each post shows the author's session label or username (if the viewer is a confirmed friend). Like/dislike buttons on each post — weighted by voter tier, but the weighting is invisible to participants until the facilitator reveals it. Report button on each post; a single report spoilers the post (content hidden behind a click-to-reveal overlay). Post input requires the appropriate Post Key for the user's tier. **Friends-only toggle**: switches the feed to show only posts from confirmed friends, hiding the public square entirely.
|
||||
3. **Profile**: Your notes editor. Toggle visibility (requires Profile Key). Friend Key management: select target's username from the registered list, enter your Friend Key, enter their Friend Key, submit. View confirmed friends and their usernames.
|
||||
4. **Admin Panel** (`/admin`, authenticated by the 4-emoji Admin Post Key, entered once per browser session): Restrict or promote users by session label. View and distribute the current Default Post Key (the key is displayed on this screen so the facilitator can read it out, write it on a board, or whisper it). Trigger attack simulations (see Section 7). View raw database. Unrestrict users. Show/hide the join QR code.
|
||||
|
||||
**Platform View — "WHAT THE PLATFORM SEES"** (bottom of admin panel): A surveillance dashboard designed for mid-session or end-of-session projection. Three panels:
|
||||
- *Deanonymization table*: every session label alongside real username, tier, post count, and net score — one screen collapses the pseudonymity the whole session was built on.
|
||||
- *Social graph*: all handshake pairs (CONFIRMED / PENDING). The platform has always known the full network topology; participants knew only their own connections.
|
||||
- *Behavioral correlations*: who voted for whom and who reported whom, cross-referenced against confirmed friendships. Shows that the platform can infer relationships from behavior alone — the metadata lesson. A `[FRIENDS]` badge appears where a correlation pair are confirmed friends.
|
||||
|
||||
---
|
||||
|
||||
## 7. Hardware Constraints: Honest Assessment
|
||||
|
||||
### Primary Target: Android via Termux
|
||||
|
||||
- **Minimum viable server device**: Any Android phone running 7.0+ with 1GB+ RAM.
|
||||
- **Realistic capacity**: Flask on SQLite on a budget Android phone comfortably handles 15-20 concurrent browser clients on a local hotspot. This matches typical classroom size.
|
||||
- **Battery**: A full session (2-3 hours) will drain the server device. Keep it plugged in.
|
||||
- **Background kill**: Android aggressively kills background apps. The facilitator must ensure Termux has a persistent notification and battery optimization is disabled for Termux in device settings.
|
||||
|
||||
### Stretch Goal: iOS via iSH
|
||||
|
||||
- **What works**: iSH runs Alpine Linux on iOS 11+, which covers iPhone 7 and newer. Python 3 installs. Flask runs. It can serve pages on a local network.
|
||||
- **What's slow**: iSH emulates x86 on ARM. Everything runs at a fraction of native speed. Server response times will be noticeably sluggish.
|
||||
- **What doesn't work**: iOS aggressively suspends background apps. The iSH app must stay in the foreground with the screen on. This is fragile for a server role.
|
||||
- **Recommendation**: Use iOS devices as *clients* (browsers), not servers. If an Android device is available, it should be the server.
|
||||
|
||||
### Legacy Apple Devices as Teaching Props and Attack Platforms
|
||||
|
||||
The iPad Air (1st gen) and iPhone 7 are not good Meshagora servers, but they have two valuable roles: demonstrating hardware repurposing and running adversarial simulations.
|
||||
|
||||
**Tinkering and Repurposing Demos:**
|
||||
|
||||
- **iSH install walkthrough**: Show participants that a Linux terminal can run on an old iPhone. The act of installing and typing `python3` on a phone is itself a teaching moment — the device is more than what Apple says it is.
|
||||
- **Client participation**: Old iOS devices work fine as browsers connecting to Meshagora. They're participants in the network, not infrastructure.
|
||||
- **Hardware teardown context**: "This phone has a GPS, accelerometer, gyroscope, barometer, camera, microphone, and a multi-core processor. It cost someone $800 in 2016. It now costs $40. What can you do with it?" This connects directly to Section 1 of the workshop.
|
||||
- **Limitations as lessons**: The fact that iOS *restricts* background processes, server roles, and sideloading is itself a teaching point about platform control and walled gardens.
|
||||
|
||||
**The Attack Platform (Phase 2 Tool):**
|
||||
|
||||
Legacy iOS devices are ideal for running adversarial simulations because they're physically separate from the server, visible to the class, and just capable enough to fire HTTP requests.
|
||||
|
||||
- **MVP approach — Admin Panel as remote control**: The facilitator opens the Agora's `/admin` route in Safari on the iPad. From there they can trigger canned disruptions: create sock puppet accounts that flood the feed, rotate the Default Post Key, restrict users, and display the raw database. The iPad becomes a visible "attack console" — the class can see the facilitator tapping buttons on a separate device and watch the effects propagate to their screens in real time.
|
||||
- **Post-MVP approach — Visible scripts**: A standalone Python script running in iSH on the iPad or iPhone 7 that hits Meshagora's API endpoints as fake users. The facilitator shows the script running on the device's screen: "This is what a bot looks like. These are the HTTP requests it sends. This is how a sock puppet account works." iSH is slow, but for sending POST requests to a local Flask server, it's more than adequate. Learners can read the code, see it execute, and correlate each request with what appears in the feed.
|
||||
- **Why a separate device matters**: Running attacks from the same device as the server muddles the lesson. A physically distinct attack device teaches that threats come from outside your system, through the same network interface that legitimate users connect through. The iPad sitting on the teacher's desk running attack scripts is a tangible threat model.
|
||||
|
||||
### What Was Cut (and Why)
|
||||
|
||||
- **QPython**: Largely abandoned, unreliable. Not included.
|
||||
- **BeeWare/Kivy packaging**: Building .apk files is out of scope for a classroom exercise.
|
||||
- **Local LLM inference**: Not viable on target hardware. An iPhone 7 with 2GB RAM cannot usefully run even TinyLlama through iSH's x86 emulation layer.
|
||||
- **Captive portal**: Requires root or custom firmware on most Android devices. The QR code approach solves the same discovery problem without this complexity.
|
||||
|
||||
---
|
||||
|
||||
## 8. Facilitator Guide Notes
|
||||
|
||||
### Before the Session
|
||||
|
||||
- Test the full setup on the actual server device at least once.
|
||||
- Pre-install Termux, Python, Flask, and qrcode library.
|
||||
- Set up the attack device (iPad/iPhone): bookmark the Admin Panel URL in Safari, or pre-install iSH with attack scripts if using the richer approach.
|
||||
- Print 2-3 backup QR codes in case the on-screen code is hard to scan.
|
||||
- Prepare a simple one-page handout: "Join the WiFi called [X], scan this code."
|
||||
- Decide in advance which disruptions you'll run in Phase 2 and in what order.
|
||||
|
||||
### During the Session
|
||||
|
||||
- **Announce the threat model early**: "This system will be stressed during our session. Part of your job is to notice when that happens and respond."
|
||||
- **Control pacing**: Don't rush from Phase 1 to Phase 2. Let participants settle into the open square before introducing disruption. 10-15 minutes of calm posting first.
|
||||
- **Let pseudonymity develop naturally**: Don't explain the friend-reveals-username mechanic upfront. Let participants discover that their friends' posts suddenly have names while strangers remain anonymous. The surprise is the lesson.
|
||||
- **Use the like/dislike pinning early**: Once a few posts accumulate, point out what's pinned at top and bottom. Ask: "Who decided what you see first? You did — collectively. Is the result good?" This is the gentlest possible introduction to algorithmic ranking.
|
||||
- **Reveal vote weighting later**: After participants have internalized the ranking, reveal that trusted users' votes count double and restricted users' votes count for nothing. Ask: "Did you notice? Does this change how you feel about what's pinned at the top?" This is the covert-influence lesson — equal-looking buttons producing unequal outcomes.
|
||||
- **Demonstrate the database**: At some point, show participants the raw SQLite file via the Admin Panel. Let them see their "private" notes and "secret" usernames in plain text. This is the single most impactful teaching moment in the session.
|
||||
- **Restrict someone and let the ripple hit**: When you restrict a user, the Default Post Key rotates silently. Don't announce it. Wait for participants to discover their posts are failing and come to you. The queue of confused people at the facilitator's desk is the lesson. Discuss afterward: "Why did your key break? Who got restricted? Was it the admin or was it the community? Is this fair?"
|
||||
- **Let the community report mechanic play out**: Don't intervene when spoilered posts start appearing. If the class auto-restricts someone unfairly, that's a better teaching moment than preventing it. Discuss afterward: "Was that just? What would due process look like? Who decides what's 'bad behaviour' when the majority rules?"
|
||||
- **Key distribution is social, not automatic**: When the Default Post Key rotates, participants must come to you. You choose how to distribute: announce to the whole room, whisper to individuals, write it on the board, or make them earn it. Each distribution method teaches something different about platform access and information asymmetry.
|
||||
- **Watch for the friends-only retreat**: When participants start switching to friends-only view, the public square will thin out. Voting activity drops, pinned posts stagnate, the feed feels dead. Point this out: "What happened to the public square? Where did everyone go? What does it mean when the engaged users leave?" This is platform death spiral in miniature.
|
||||
- **Resist fixing things for participants**: When the noise hits, point participants toward features they haven't used yet — but don't implement anything for them. The learning is in the doing.
|
||||
|
||||
### After the Session
|
||||
|
||||
- Stop the server. The database is gone. Emphasize this: "Everything we built today is gone. What remains is what you learned."
|
||||
- In multi-session courses: the clean slate is intentional. Learners carry forward the social norms, agreements, and competence they built — but the technical environment resets. They rebuild faster each time. The knowledge is in the humans, not the machine.
|
||||
- Connect to the broader workshop: "The platforms you use every day have this same structure — servers, databases, keys, feeds, ranking algorithms, moderation tiers. The difference is you can't see inside them. Now you know what's in there."
|
||||
|
||||
---
|
||||
|
||||
## 9. Minimum Viable Product
|
||||
|
||||
The MVP is the smallest buildable version that delivers the pedagogical arc. Everything below this line must work before any extensions are considered.
|
||||
|
||||
### MVP Features
|
||||
|
||||
1. User creation with secret username (case-sensitive string) and system-assigned sequential session label.
|
||||
2. Pseudonymous public square: posts show session labels to non-friends, usernames to confirmed friends.
|
||||
3. Tiered Post Keys: Admin (4-emoji), Trusted (2-emoji), Default (2-emoji, rotating), and Restricted posting (admin co-sign required). Admin Post Key also authenticates the Admin Panel.
|
||||
4. Like/dislike on posts with tier-weighted scoring (Trusted +2, Default/Admin +1, Restricted 0). Highest-scoring pinned to top, lowest to bottom. Weight locked at time of vote.
|
||||
5. Per-post report button. Single report spoilers the post (click-to-reveal). 51% unique reporters across a user's posts auto-restricts them. Facilitator can restrict on a single report. Reported users are notified but not told who reported them. Unrestriction is admin-only.
|
||||
6. Friends-only feed toggle: hides the public square, shows only posts from confirmed friends.
|
||||
7. Profile page with notes editor (requires Profile Key to edit).
|
||||
8. Notes visibility toggle (public/private, requires Profile Key).
|
||||
9. Friend Key handshake: select target from registered username list, enter your key, enter their key, system concatenates and validates the linked palindromic pair. Friendship reveals username on the public feed.
|
||||
10. Private note viewing for confirmed friends.
|
||||
11. Admin Panel (authenticated by 4-emoji Admin Post Key): user management, distribute new Default Post Key, attack triggers, unrestriction, raw database view, QR show/hide.
|
||||
12. QR code generation on server startup with show/hide control.
|
||||
13. Session reset on server restart. Learners carry forward social norms and knowledge; the database does not persist.
|
||||
|
||||
### MVP Delivers
|
||||
|
||||
- The full Phase 1–4 pedagogical arc.
|
||||
- Tangible experience of pseudonymity, trust hierarchies, platform moderation externalities, algorithmic content ranking, community justice (and its failures), and the retreat-to-trust-network dynamic.
|
||||
- Both top-down (admin) and bottom-up (community report) moderation in the same system, with visible tradeoffs.
|
||||
- A working micro-fediverse that runs on a single old phone, attacked from an old iPad.
|
||||
|
||||
---
|
||||
|
||||
## 10. Extensions
|
||||
|
||||
### Implemented (Beyond MVP)
|
||||
|
||||
**Platform View — Admin Surveillance Dashboard** *(implemented)*
|
||||
Described in Section 6. No schema changes — pure SQL joins on existing tables, rendered as three panels at the bottom of the Admin Panel. See Section 6.4 for full description.
|
||||
|
||||
### Future Extensions (Post-MVP)
|
||||
|
||||
These are documented for future development. None are required for the core experience.
|
||||
|
||||
### Chaos Bot (Advanced)
|
||||
The MVP admin panel supports basic sock puppet and flood attacks. This extension adds autonomous adversarial agents: bots that adapt their behaviour based on the feed state (e.g., targeting the most-liked post with dislikes, mimicking a specific user's posting style, or gradually escalating provocative content). Teaches pattern recognition and the difference between scripted spam and adaptive manipulation.
|
||||
|
||||
### Privacy Filter Workshop
|
||||
Instead of a simple visibility toggle, participants write or configure their own filter rules (e.g., "show my notes only to users with reputation > X"). Teaches algorithmic moderation.
|
||||
|
||||
### Reputation System
|
||||
A community-driven trust score based on peer endorsements. Teaches how reputation systems work (and how they can be gamed).
|
||||
|
||||
### Auto-Trust by Social Graph
|
||||
A facilitator-toggled mechanic: when enabled, any user with confirmed friendships exceeding half the active participants is automatically promoted to TRUSTED tier. This teaches that platforms often grant authority based on network metrics (follower count, engagement, connections) rather than demonstrated competence. Best introduced mid-session after participants have experienced manual trust promotion, so the class can contrast the two models and discuss what changes when popularity becomes power. Creates a perverse incentive to collect friendships as tokens rather than trust relationships — surfacing that incentive *is* the lesson.
|
||||
|
||||
### Offline Knowledge Library
|
||||
Integrate Kiwix (offline Wikipedia/Project Gutenberg) as a second service on the server device. The "survival information" component — demonstrating that a phone can be a library, not just a communication device.
|
||||
|
||||
### Cross-Device Federation
|
||||
Run two Meshagora instances on two devices on the same network. Posts federate between them. Directly demonstrates the Fediverse model with physical hardware participants can see and touch.
|
||||
|
||||
### Agent Integration
|
||||
Use an AI coding agent to generate the Flask server code from this spec, with participants auditing the output. Teaches code review and AI literacy simultaneously.
|
||||
|
||||
---
|
||||
|
||||
## 11. Specification Status
|
||||
|
||||
This is a **v2.3 hybrid human-model design document**. It has been:
|
||||
|
||||
- Generated through conversation with Gemini (v1.0 and v1.1).
|
||||
- Reviewed and critiqued by Opus for technical accuracy, framework alignment, and scope control.
|
||||
- Refined with corrected framework language (Safety/Sanity/Serenity), linked palindromic friend-key mechanics, honest hardware constraints, iOS tinkering use cases, and integration with existing 3SC workshop materials.
|
||||
- Extended with tiered post keys, like/dislike ranking, pseudonymous identity, community reports, friends-only feed, and legacy iOS as attack platform.
|
||||
- Final review pass: resolved agent-facing ambiguities (admin auth, handshake flow, key distribution, vote weight locking, report notification, active participant threshold, Phase 3 feature-gating), reduced duplication between Sections 4 and 5, documented pseudonymity implications of username list.
|
||||
|
||||
It is ready for:
|
||||
|
||||
- Implementation by a coding agent (targeting a single `app.py` file).
|
||||
- Human audit of the generated code.
|
||||
- Pilot testing on actual legacy hardware.
|
||||
|
||||
It is **not** a finished product. It is a buildable specification for a prototype.
|
||||
|
||||
---
|
||||
|
||||
*Design note: This specification is intended to be stored on the server device itself. The documentation is the first artifact in the lab.*
|
||||
19
ToolsnToys/ToolsnToys-JL-notes.md
Normal file
19
ToolsnToys/ToolsnToys-JL-notes.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# TNT: Tools N Toys - JL notes
|
||||
|
||||
---
|
||||
|
||||
in here are *-tools.md, where * is the category name. the files are lists of links. there are also folders, and other files which are themselves, tools or toys.
|
||||
|
||||
---
|
||||
|
||||
render links as cards with some useful information and a visit button.
|
||||
|
||||
render html in a full width iframe
|
||||
|
||||
if possible to load an application into the browser, go ahead, elsewise make a card with info and a download button.
|
||||
|
||||
---
|
||||
|
||||
tools are downloadable. links are not.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user