Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/ISSUE_TEMPLATE/bug.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Bug report
description: Something the app does or shows is wrong.
title: "bug: "
labels: ["bug"]
body:
- type: textarea
id: what-happened
attributes:
label: What happened
description: One or two sentences. What did you see, and what did you expect?
placeholder: "Tapped Send on devnet; spinner ran 60s then 'Couldn't reach devnet' even though Wi-Fi was up."
validations:
required: true
- type: textarea
id: repro
attributes:
label: Steps to reproduce
description: Minimum steps so someone else can hit the same state.
placeholder: |
1. Open Wallet
2. Tap Send
3. ...
validations:
required: true
- type: dropdown
id: device
attributes:
label: Device
options:
- Solana Seeker
- Stock Android (Pixel / Samsung / OnePlus / other)
- iPhone
- Other (note OS in the description)
validations:
required: true
- type: input
id: os
attributes:
label: OS version
placeholder: "Android 15 / iOS 18.2"
validations:
required: false
- type: dropdown
id: network
attributes:
label: Network state at time of bug
options:
- Wi-Fi
- Cellular
- Mesh only (no internet)
- Airplane mode
- Mixed / transitioning
validations:
required: true
- type: input
id: build
attributes:
label: App build / version
description: From Settings → app version, or the APK filename.
placeholder: "1.0.2"
validations:
required: false
- type: textarea
id: logs
attributes:
label: Logs / screenshots (optional)
description: Paste relevant logs or attach a screenshot. Redact addresses / seeds.
validations:
required: false
40 changes: 40 additions & 0 deletions .github/ISSUE_TEMPLATE/feature.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Feature request
description: A new capability or UX you'd like to see.
title: "feat: "
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: The problem
description: What real-world situation triggered this? Skip "it would be nice if" — describe the gap you actually hit.
placeholder: "Sending SPL tokens isn't supported, so I can't move USDC to a friend without bridging to SOL first."
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed behavior
description: Sketch the flow. Screens / states / inputs.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Other ways to solve this you thought about, and why they didn't fit.
validations:
required: false
- type: dropdown
id: scope
attributes:
label: Where it lives
options:
- Wallet (send / receive / tokens)
- Messages / channels
- Mesh / network / beacon
- Settings / identity / recovery
- Onboarding
- Other
validations:
required: true
39 changes: 39 additions & 0 deletions .github/ISSUE_TEMPLATE/privacy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Privacy / security concern
description: Something in the app, README, or behavior that you think contradicts the privacy claims.
title: "privacy: "
labels: ["privacy", "security"]
body:
- type: textarea
id: claim
attributes:
label: The claim
description: Which AnonMesh claim, doc line, or UI label looks wrong? Quote the exact wording.
placeholder: 'README says "routes through nothing" — but in mesh mode it routes through a beacon-relay.'
validations:
required: true
- type: textarea
id: evidence
attributes:
label: What you observed
description: File line, screenshot, log line, or repro steps that point to the gap.
validations:
required: true
- type: dropdown
id: severity
attributes:
label: Severity
options:
- "Misleading copy / labeling (P1)"
- "Consent gap (P0)"
- "Information leak / metadata leak (P0)"
- "Crypto / auth bypass (P0)"
- "Not sure"
validations:
required: true
- type: textarea
id: disclosure
attributes:
label: Responsible disclosure
description: For exploit-class issues, please email instead of filing here. Note your contact and we'll coordinate.
validations:
required: false
58 changes: 58 additions & 0 deletions .github/workflows/honesty-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Honesty check

# Greps the diff for theatre phrases that contradict what the app actually
# does. See ROADMAP § 0.8 + AUDIT § 2 (Category C) for the rationale.
#
# Failure means a present-tense claim about an unshipped capability landed in
# code or docs without a "PREVIEW" / "coming soon" qualifier. Either:
# - wrap the UI in <PreviewBadge> / <PreviewedActions>, OR
# - rewrite the copy in coming-soon framing, OR
# - if the feature actually shipped, add it to ALLOWED_PHRASES below.

on:
pull_request:
paths:
- "mobile_app/**"
- "README.md"
push:
branches: [v3]
paths:
- "mobile_app/**"
- "README.md"

jobs:
honesty:
runs-on: ubuntu-latest
env:
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PUSH_BEFORE_SHA: ${{ github.event.before }}
EVENT_NAME: ${{ github.event_name }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Grep diff for banned theatre phrases
run: |
set -euo pipefail
if [ "$EVENT_NAME" = "pull_request" ]; then
BASE="$PR_BASE_SHA"
else
BASE="$PUSH_BEFORE_SHA"
fi
if [ -z "${BASE:-}" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ]; then
echo "No base ref to diff against; skipping honesty check."
exit 0
fi
# Banned phrases. We grep added lines only so historical text doesn't fail the check.
PATTERNS='ARCIUM|MPC 3/3|MPC-3/3|Confidential Offline|JITO_RATE|stealth mode active|routes through nothing|encrypted with AES-256'
DIFF=$(git diff --unified=0 "$BASE"...HEAD -- 'mobile_app/**' 'README.md' | grep -E '^\+[^+]' || true)
HITS=$(echo "$DIFF" | grep -E "$PATTERNS" || true)
if [ -n "$HITS" ]; then
echo "Honesty check failed. Banned theatre phrase(s) in diff:" >&2
echo "$HITS" >&2
echo "" >&2
echo "Wrap in PreviewBadge / PreviewedActions, or reframe as coming-soon." >&2
exit 1
fi
echo "Honesty check passed."
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@

# anonmesh

**No servers. No accounts. No surveillance. No bullshit.**
**No centralized servers. No accounts. No surveillance. No bullshit.**

Encrypted mesh messaging and Solana payments for Android and iOS works on a mountaintop, in the middle of the ocean, in a blackout, or anywhere the internet doesn't reach. If two devices can find each other over *anything*, Bluetooth, LoRa radio, LAN, the internet, they can talk. No carrier. No cloud. No one in the middle.
Encrypted mesh messaging and Solana payments for Android and iOS that works on a mountaintop, in the middle of the ocean, in a blackout, or anywhere the internet doesn't reach. If two devices can find each other over *anything*Bluetooth, LoRa radio, LAN, the internetthey can talk. No carrier required. No cloud account. Community-run public TCP relays are pre-configured for bootstrap and can be removed in settings.

You own the keys. You own the network. Nobody can take it from you.
You own the keys. You own your share of the network. Nobody can take it from you.

---

## Why

Every messaging app you use today is a surveillance platform with a chat UI bolted on. Signal requires a phone number. WhatsApp is Meta. Telegram stores your messages on their servers. Even "decentralized" apps route through centralized relays, CDNs, or DNS.

anonmesh routes through **nothing**. It uses [Reticulum](https://reticulum.network), a cryptographic transport network that works over whatever physical medium is available, and [LXMF](https://github.com/markqvist/LXMF) for message delivery. There is no account creation. There is no server to subpoena. There is no company to comply with a warrant. Your identity is a keypair that lives only on your device.
anonmesh routes through whatever's available — peer-to-peer first, beacon-relays only when no direct path exists. It uses [Reticulum](https://reticulum.network), a cryptographic transport network that works over whatever physical medium is available, and [LXMF](https://github.com/markqvist/LXMF) for message delivery. There is no account creation. There is no central server to subpoena. There is no company to comply with a warrant. Your identity is a keypair that lives only on your device.

---

## Messaging

### Direct Messages

Scan a QR code or paste an LXMF hash. That's it. The message is encrypted end-to-end with X25519 + AES-256 before it leaves your device. If the peer is offline, the message queues locally and delivers the moment a path opens, over any interface, across any number of hops.
Scan a QR code or paste an LXMF hash. That's it. The message is encrypted end-to-end with X25519 + AES-128 + HMAC per the [Reticulum spec](https://reticulum.network/manual/understanding.html) before it leaves your device. If the peer is offline, the message queues locally and delivers the moment a path opens, over any interface, across any number of hops.

- No phone number. No username. No account.
- Peer status (online / offline, hop count, interface) live in the thread header
Expand Down Expand Up @@ -70,9 +70,9 @@ Non-custodial. Keys never leave the device.
- Send SOL or SPL tokens to any address or mesh peer handle
- Receive via QR
- On-chain activity history
- Swap and yield (coming soon)
- Swap, yield, and confidential offline transfers (coming soon — roadmap previews are labeled in-app)

No analytics. No RPC phoning home beyond what's needed to read the chain and submit transactions. RPC endpoint is yours to configure.
No analytics. No telemetry. RPC calls are limited to reading the chain and submitting transactions, and the endpoint is yours to configure. On mesh-RPC mode, requests are proxied through a beacon-relay (a future release will encrypt the JSON-RPC payload end-to-end to the relay).

---

Expand Down
2 changes: 1 addition & 1 deletion mobile_app/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"expo": {
"name": "anonmesh",
"slug": "anonmesh",
"version": "1.0.1",
"version": "1.0.2",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "anonmesh",
Expand Down
38 changes: 38 additions & 0 deletions mobile_app/components/primitives/PreviewBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from "react";
import { StyleProp, StyleSheet, View, ViewStyle } from "react-native";

import { Pill } from "./Pill";

interface PreviewBadgeProps {
/** Tail copy appended after "PREVIEW · ". Defaults to "coming soon". */
label?: string;
/** Alignment inside the wrapper. Defaults to "flex-start". */
align?: "flex-start" | "center" | "flex-end";
style?: StyleProp<ViewStyle>;
}

/**
* Canonical PREVIEW badge for roadmap placeholders. Render above any placeholder
* UI to mark it as forward-looking (paired with <PreviewedActions> on the CTAs
* inside that placeholder).
*
* Visual shape matches the existing pendingCosigns + BeaconRegistry hero CTA
* pattern.
*/
export function PreviewBadge({
label = "coming soon",
align = "flex-start",
style,
}: PreviewBadgeProps) {
return (
<View style={[styles.wrapper, { alignItems: align }, style]}>
<Pill label={`preview · ${label}`} tone="amber" />
</View>
);
}

const styles = StyleSheet.create({
wrapper: {
width: "100%",
},
});
Loading
Loading