Commit Graph
215 Commits
Author SHA1 Message Date
Claude 9cc19c60ca fix(concord): three defects found auditing this branch's own changes
Self-review of the diff before merge. One of these is a real correctness bug in
the B2 fix as shipped.

The resolver stopped after two passes, which left the mask a pass resolved UNDER
disagreeing with the banlist that pass produced — and the disagreement is not
cosmetic. A moderator whose only ban came from an admin the owner banned
concurrently is released by pass 2, correctly; but pass 2 had already dropped
her editions, because she was on pass 1's list. The fold then reported her as a
moderator in good standing whose promotions had silently vanished, and did so
deterministically, so she never got them back. resolve() now iterates until the
mask and the resulting banlist agree.

The mask cannot simply be assumed to shrink, which is why this is bounded rather
than proven monotone: masking an author can strip a THIRD member's role, which
drops their rank to roleless, which lets a junior BAN holder who previously could
not reach them ban them after all. The loop keeps its last pass if it does not
settle within the cap — still better than the two-pass answer, and it always
terminates. Real communities settle on the first or second pass, and the
skip-if-no-banned-author guard means most never enter the loop at all.

boundRecipients could exceed its own budget while reporting that it had capped
at it, because the roster was added with filterTo before the budget loop ran.
The roster now goes in whole deliberately — it is owner-rooted and cannot be
padded from outside, and dropping an admin to make room for a stranger inverts
the point — and the log reports what was actually kept and dropped.

mintConcordInvite started requiring a session, which the owner's own invite
button would not have on a cold start, since sessions are built asynchronously
off the joined list. The owner is proven by the community id, so they are read
off the entry; everyone else still needs the folded roster.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DrJhpFhhLjuDJQNkGvYMGj
2026-08-09 16:38:35 +00:00
Claude 54d3bfc84a perf(quartz): skip the resolver's second pass when it cannot change anything
The two-pass ban-aware fold doubles resolve(), which runs once per held epoch in
controlFloorsLocked plus once in fold, on a client that re-folds the whole buffer
from scratch on every Control Plane change. So the second pass is now skipped
unless a banned member actually authored a Control edition — not merely when the
banlist is empty. Bans overwhelmingly land on plain members who hold no role and
write nothing, and for those pass B is provably identical to pass A. Armada's
fold checks the same condition.

Measured over ConcordCommunityState.fold (throwaway benchmark, not committed;
226 and 2059 editions, 200 reps after warmup). Pass A is byte-for-byte the old
algorithm, so the single-pass rows are the before-numbers:

  226 eds, no bans                          1457 us
  226 eds, 20 bans, none authored            994 us
  226 eds, 20 bans, one authored -> pass B  1881 us
  2059 eds, no bans                         2194 us
  2059 eds, 50 bans, none authored          2001 us
  2059 eds, 50 bans, one authored -> pass B 5697 us
  2059 eds, with floors (B1's arm)          2015 us

So the common case is free, and B1's chain-first compaction arm is not
measurable — the floored fold matches the unfloored one. A banned staffer costs
~2-3x, which is the price of the fix and is paid only under the attack.

The audit records this, plus the standing opportunity it surfaced: we have no
fold memoization where Armada does, which predates this work and would absorb
the pass-B cost too. Not done here — that is a change to make on its own merits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DrJhpFhhLjuDJQNkGvYMGj
2026-08-09 16:09:53 +00:00
Claude 6147f72c81 docs(concord): check the audit against Armada, and correct two conclusions
Read gitlab.com/soapbox-pub/armada src/concord-v2/ against every finding. Two
conclusions change.

B2 is NOT consensus-affecting, and the warning in the last commit was wrong.
Armada's foldControlState already runs the same bounded two-pass — fold once,
take the banlist, re-fold with banned authors' editions excluded — arrived at
independently, same shape, same CORD-04 §4 justification in the comment. This
change brings us into line rather than out of it. One narrower divergence
remains: they keep pass 1's banlist as final, we recompute it in pass 2, so a
banned admin's mass-ban still stands for them and is dropped by us. Both
defensible; ours closes an attack theirs leaves open, and the self-erasure they
guard against is unreachable under the rank rule.

A2's fork is resolved, in favour of the fix having been necessary.
useLinkRefreshWatch2 re-posts every invite bundle on each epoch change, so the
"if anything re-mints at a stable coordinate" branch is what actually happens —
in any cross-client community a removed member's Amethyst client would have
pulled the new root within fifteen minutes. Their catch-up is push instead:
a privileged member sends a direct invite carrying the fresher root, so a human
authorizes each re-admission, and useBanSelfRemove2 has a banned member's own
client silently drop the community. The liveness half stands and now has two
concrete options rather than an open question.

Also recorded: B1 is present in Armada unfixed, in exactly the same place
(bootstrapHead is unbounded, headCandidates uses it, pickHead raises the floor) —
the second bug both clients share by reading one section the same way, so it goes
to them in writing like the rank rule did. A1 was ours alone; they gate invite
creation on CREATE_INVITE in both the hook and the page. C1 is unchanged on their
side. A4 is a shared gap. And a divergence in the other direction: their banlist
takes only the head's content, with no §4 re-heal union, so we honor concurrent
bans they drop.

B4 is marked unchecked rather than guessed at — I could not locate their
recipient-set construction with confidence.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DrJhpFhhLjuDJQNkGvYMGj
2026-08-09 15:54:55 +00:00
Claude 4e99aafb59 fix(quartz): honor the banlist against the Control Plane itself
B2 in docs/concord-soft-ban-audit.md, plus B4's bound and the audit's status pass.

hasPermission was ban-aware; the resolver's own ROLE/GRANT/BANLIST gates were not,
and could not be as written — the roles/grants fixpoint settled before `banned`
was computed at all. So half the Control Plane honored a ban and half was blind
to it, and a banned staffer still holding control_root kept the whole roster:
banning everyone beneath them, revoking the surviving moderators, retiring the
roles under them, and minting a fresh un-banned npub that passed every ban-aware
gate and finished the job.

resolve() is now a bounded two-pass where authority only ever shrinks. Pass A
resolves as before and yields a candidate banlist; pass B re-resolves with every
author on it treated as holding no authority. Two passes always, so it terminates
by construction, and mutual bans cannot oscillate because the rank rule makes
them unreachable — only someone who strictly outranks you may ban you, and you
cannot outrank them back. A chain-local rule would not have worked: forking the
banlist at genesis means no parent ever mentions the ban and §4's re-heal union
carries it in regardless, so the rule is a whole-pass mask rather than a
per-edition check.

This cascades, deliberately: every edition a banned member ever authored is
dropped, grants included, so banning an admin also demotes everyone that admin
promoted. That is the literal reading of CORD-04 §4 and it is what kills the
sockpuppet, but a legitimate promotion by a later-banned admin vanishes with it
and has to be re-issued. Both the cascade and its blast radius are pinned, and
the trade-off is written up in the Armada report as the answer to its own open
row 3 — which also widens the divergence recorded there: we now drop editions
they honor wherever a privileged member was banned.

B4: the Refounding recipient set is capped. allMembers() is the Guestbook ∪
observedAuthors ∪ the roster, and the first two are unbounded and
attacker-writable, so each throwaway npub someone posts from became one more
mandatory blob in the next Refounding — the attack inflating the cost of its own
remedy. The owner-rooted roster is kept first and anything dropped is logged,
never silently truncated, because a dropped member is stranded.

The nine escalation reproductions now assert the fixed behaviour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DrJhpFhhLjuDJQNkGvYMGj
2026-08-09 15:47:41 +00:00
Claude f52a8b0432 docs(concord): split the audit by what the attacker needs
Re-reviewed every finding against the shipping app rather than against the
protocol, and split the list in two: what a banned user can do with stock
Amethyst (our bugs) versus what needs a hand-written client (fix in the fold, or
defend against). Several items moved, and the review turned up a new one that
belongs at the top.

A1 is new and is the realistic attack. mintConcordInvite checks only that the
account is writeable and that we hold the community — no CREATE_INVITE, no
banlist — and unlike the Edit and channel buttons next to it, the invite
IconButton carries no guard at all. A banned user stays in the app, taps
person-add, and shares a working link to the community. The mint publishes a
fresh link signer, so revoking the links they were given does not touch the ones
they make; and because the bundle is a standalone kind-33301 outside the Control
Plane, the CREATE_INVITE bit the fold enforces on INVITE_* entities never
applies to the actual invite mechanism.

A3 is the general form: every moderation verb checks isWriteable() and the
Control write key and nothing else, so authority lives in the composable that
draws the button — and those gates use effectivePermissions, which is ban-blind.
Ban and Remove survive only because a second, unrelated condition routes through
the ban-aware canActOn. refoundConcordCommunity guards itself with
effectivePermissions outright, so a banned BAN-holder can launch a Refounding
from the shipping app; honest receivers refuse it, but that is a race against
banlist propagation, not a check.

A2 moves to Part A because our own client is what performs it: the recovery
sweep runs every 15 minutes with no banlist check.

C2 (voice) is downgraded from High — ConcordBrokerToken and VoicePresence are
referenced nowhere outside quartz, so there is no shipping path to attack. It is
a note for whoever wires one up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DrJhpFhhLjuDJQNkGvYMGj
2026-08-09 01:41:52 +00:00
Claude d37e183a57 docs(concord): audit the surfaces the first pass never opened
The first pass was bounded by the Control Plane, the fold and the relay. Three
more findings from the surfaces it skipped, plus an explicit list of what is
still unexamined so the next reader knows where the edges are.

V10 is the serious one, and it forks. ConcordStrandedRecovery.isStranded takes
only (entry, bundle): no banlist check, no check that we were legitimately
re-keyed. The whole test is "the bundle at my stored invite_ref sits at a higher
epoch than I do", and the unlock token lives in the link fragment an ex-member
keeps forever. So whether a removed member walks back in depends only on whether
anything re-mints at that coordinate. Amethyst mints a fresh link signer per
invite and the Refounding neither re-mints nor revokes, so today nothing does —
which means stranded recovery never fires for anyone, and the cure that
drainConcordRekeys' KDoc points to for "a BAN-holder can evict anyone, the owner
included, by omission" does not actually exist. If any client does re-mint at a
stable coordinate, as CORD-05's design describes, then every removed member
auto-recovers the new root on the 15-minute sweep and re-announces a Guestbook
join. Either the safety net is missing or the only hard removal is undone;
which one it is needs a spec answer, not a patch.

V11: voice rooms authenticate with the channel's derived voice signer key
against a stateless SFU that holds no community secret and cannot know a banlist
exists, so a banned member keeps talking until a Refounding. V12: ingestTyping
filters on binding and self only, so they keep showing as "typing".

Checked and sound, recorded so they are not re-audited: the envelope pins
rumor.pubKey == seal.pubKey (no author impersonation), and Note.latestConcordEdit
is author-gated, so a member cannot rewrite someone else's message.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DrJhpFhhLjuDJQNkGvYMGj
2026-08-09 01:28:22 +00:00
Claude 1e6cda712d docs(concord): audit the soft-ban and Control Plane attack surface
Collects the findings from this branch into docs/concord-soft-ban-audit.md,
each marked Verified (a test reproduces it, named) or Read (follows from the
code, untested), with a suggested order of attack.

Adds the reproduction for the one finding that was still unverified, and it did
not hold up the way it was first described. Version inflation does not poison
the anti-rollback floor through the chain walk — that walk advances only to
head.version + 1 citing the head's hash, so a fresh joiner is untouched. It goes
through the COMPACTION ARM: once a client holds a floor and the entity is in the
epoch snapshot, the head comes from bootstrapHead, which is highest-version at
or above the floor with no prev, no hash and no contiguity. Version is then the
whole contest and Long.MAX_VALUE wins it permanently — the floor rises to
MAX_VALUE, no honest edition can exceed it, and a Refounding that drops the
poison falls back to EntityFloor.known, which is the poison.

That makes it the worst item on the list: unrecoverable, and authored in the
tests by a current, legitimately granted moderator — no ban, no sockpuppet, one
ordinary permission bit. compactControlPlane picks per entity by raw max version
too, so honest rotators carry it into every future epoch.

The banlist escapes only because AuthorityResolver folds it on a floor-less
chain walk and re-heals the union, so an honest ban still lands. That accident
is all that separates this from a permanently unmoderatable community, so it is
pinned by its own test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DrJhpFhhLjuDJQNkGvYMGj
2026-08-08 23:06:54 +00:00
vitorpamplonaandgithub-actions[bot] ac06d4c435 chore: sync Crowdin translations and seed translator npub placeholders 2026-08-08 14:12:34 +00:00
davotoulaandgithub-actions[bot] 8b0ea7389c chore: sync Crowdin translations and seed translator npub placeholders 2026-08-07 16:14:52 +00:00
vitorpamplonaandgithub-actions[bot] f4fc9917f8 chore: sync Crowdin translations and seed translator npub placeholders 2026-08-06 00:48:28 +00:00
davotoulaandgithub-actions[bot] 5c18cee6ad chore: sync Crowdin translations and seed translator npub placeholders 2026-08-05 20:29:50 +00:00
vitorpamplonaandgithub-actions[bot] 30742ab352 chore: sync Crowdin translations and seed translator npub placeholders 2026-08-03 04:35:55 +00:00
vitorpamplonaandgithub-actions[bot] 8678c88db6 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-29 20:15:13 +00:00
davotoulaandgithub-actions[bot] d504e43924 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-29 14:58:05 +00:00
vitorpamplonaandgithub-actions[bot] 8d877ea70d chore: sync Crowdin translations and seed translator npub placeholders 2026-07-29 12:33:41 +00:00
vitorpamplonaandgithub-actions[bot] b575f9078d chore: sync Crowdin translations and seed translator npub placeholders 2026-07-29 03:56:46 +00:00
Vitor Pamplona 1bda3ab31b v1.13.1 2026-07-28 23:17:02 -04:00
davotoulaandgithub-actions[bot] 8ce93f3700 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-28 18:15:15 +00:00
vitorpamplonaandgithub-actions[bot] 8145bedd15 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-28 17:36:46 +00:00
davotoulaandgithub-actions[bot] e149f935e1 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-28 08:08:23 +00:00
vitorpamplonaandgithub-actions[bot] 99f0443a98 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-28 01:37:22 +00:00
Vitor Pamplona 70646f4609 adds gigi to changelog 2026-07-27 19:05:55 -04:00
Vitor PamplonaandClaude Opus 5 f55b3cb6d9 docs(changelog): tighten v1.13.0 highlights and audit against commits
Highlights carried full feature descriptions that the sections below
already repeated. Reduce each to a single line and fold the detail down
into the section that owns it.

Audit the notes against all 1961 non-merge commits since v1.12.6:

- Drop the claim that WebSocket frame dispatch moved to a dedicated pool.
  It landed in a5c2a8b2c1's parent and was reverted 23 minutes later
  because the pool regressed. Replace it with the two receive-path wins
  that did ship (CachingEventDecoder, ParallelEventVerifier).
- Add the chat redesign, which had no section at all: bubbles,
  swipe-to-reply, name colors, jumbo emoji, day headers, the two-stage
  long-press sheet, and the new-conversation chooser.
- Add in-app podcast authoring, which the Podcasts section omitted in
  favour of consumption only.
- Promote the resource-usage ledger out of a single Wallet line into its
  own section, alongside the background-service master switch and
  memory-pressure trimming.
- Add large-screen support, NIP-85 nicknames, Birdstar and PS1 cards,
  compose signature, Marmot group icons, and other unreferenced work.

Remove the Upgrading section. No prior release has one, and all three of
its items were consequences of features documented further down, so they
now sit with the feature that causes them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 19:03:09 -04:00
Claude 9df51641ea docs(changelog): split v1.13.0 fold-in notes into shorter sentences
Break the newly added BOLT12, Buzz Agent Work board, Blossom, Cashu,
NWC, search-indexing, and geode bullets into short verb-first sentences,
removing em-dash run-ons to match the changelog house style.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017hmbpUJPxrsN4m85Kwemcw
2026-07-27 21:06:24 +00:00
Claude 6c1412b7c9 docs(changelog): fold post-2026-07-24 work into v1.13.0 notes
Adds the BOLT12 payments & zaps (NIP-B1) work, the Buzz Agent Work
board / workflow runner, Concord message editing, the Blossom gallery
+ importer, Cashu P2PK-token claiming, NWC NIP-44/deep-link/notify
improvements, wider NIP-50 search indexing, and the geode release
pipeline + pluggable event store, plus assorted fixes. Credits dergigi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017hmbpUJPxrsN4m85Kwemcw
2026-07-27 20:55:39 +00:00
vitorpamplonaandgithub-actions[bot] 9855abda64 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-27 12:59:14 +00:00
vitorpamplonaandgithub-actions[bot] 6bf3c11173 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-26 21:18:45 +00:00
vitorpamplonaandgithub-actions[bot] f088dade41 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-25 13:44:35 +00:00
Claude fa2179ac19 docs(changelog): fold post-2026-07-20 work into v1.13.0 notes
Adds Buzz agent workspaces, the push-notification redesign, notifications
tab paging, desktop moderation & safety plus NIP-88 polls, DM report
warnings, Concord cold-boot/rank-gating fixes, SQLite search/relay
performance, amy git NIP-34 parity and amy buzz, the Buzz quartz protocol
surface, geode index work, and JVM 17 / CI / bidi build items.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018o1pqCDfqqSu2UHRPuqTK1
2026-07-24 15:50:08 +00:00
vitorpamplonaandgithub-actions[bot] db0ff71432 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-21 14:00:35 +00:00
Vitor PamplonaandClaude Opus 4.8 8913af7a79 fix(concord)!: enforce CORD-04 rank gating on the Banlist fold
Closes the privilege escalation: any BAN holder could ban the authorities above
them — including the owner — because the Banlist gate checked only the BAN bit.
Once banned, a member loses all authority (`hasPermission` is `!isBanned && ..`)
and honest clients drop their events, so a single edition from the most junior
moderator permanently silenced every admin above them.

CORD-04 §3 requires the rank half: "One hard rule binds every action: the actor
must hold the required bit and strictly outrank its target — equal cannot act on
equal (an admin cannot ban a peer admin)", restated as §5 step 3. Only §4, which
defines the Banlist, states the bit half alone — which is why both this client
and Armada shipped the same rank-blind gate.

§3 is stated per TARGET while the Banlist is one whole-list document, so it is
enforced as a DELTA rule: an edition may only add or remove npubs its signer
strictly outranks, judged against the roster settled behind it; the owner is
never a valid target (position 0 is "supreme and unremovable"); and entries the
signer may not act on are IGNORED rather than rejecting the edition, so one bad
entry cannot discard the bulk-ban §4 recommends as the collision remedy, and a
rogue cannot grief the list by forcing rejections.

ConcordModeration.currentBanned now reads the honored banlist through the
resolver instead of decoding the raw head. Besides picking up the fork healing
it was missing, this closes a laundering path: our own next ban/unban would
otherwise re-publish an entry our fold refuses, under our signature.

BREAKING (consensus): Armada has not shipped this rule, so banlists can differ
between clients until it does — we now ignore a ban Armada honors whenever the
signer did not outrank the target. Shipping the spec-conformant behaviour was
judged better than continuing to honor an escalation. Write-up to send upstream
is docs/concord-banlist-rank-conformance.md.

The three tests added in 0ae6bc6698 as @Ignore-d documentation now pass and are
un-ignored; two companions (a moderator still bans a plain member, the owner
still bans anyone) passed throughout and pin what the fix had to preserve.
Full :quartz:jvmTest and :commons:jvmTest suites green.

Still open and documented, not addressed here: a banned BAN holder can lift
their own ban (a fixpoint-ordering question that needs a spec ruling), and a
forked ban survives an unban that does not chain onto it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:53:11 -04:00
Vitor PamplonaandClaude Opus 4.8 ea6762b137 docs(concord): Banlist rank gap is a conformance bug, not a spec gap
Correcting the previous commit's reasoning. It concluded from the two
implementations that CORD-04 does not rank-gate the Banlist and that enforcing
it would be a unilateral divergence. Reading the actual spec
(github.com/concord-protocol/concord, not in the Armada repo) shows the
opposite: §3 is normative and binds "every action" — "the actor must hold the
required bit and strictly outrank its target — equal cannot act on equal (an
admin cannot ban a peer admin)" — and §5 step 3 restates it. Banning is the
example the rule itself picks.

Only §4, the section defining the Banlist, states the bit half alone. Both
independent implementations read §4 in isolation and made the same mistake,
which is evidence about the section rather than about the readers.

So the fold fix is spec-mandated. It remains consensus-affecting (we would
ignore bans Armada honors until they ship), so it wants coordination rather
than a race, and the fold is still unchanged here.

Adds docs/concord-banlist-rank-conformance.md to share upstream: verbatim spec
citations, both implementations' gates, a delta-based rule that makes the
per-target requirement expressible against a whole-list entity, and two further
reproduced findings — a banned BAN-holder can lift their own ban (so bans do
not stick against any BAN holder), and a forked ban survives an unban that does
not chain onto it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-20 12:05:21 -04:00
Vitor PamplonaandClaude Opus 4.8 996b800da5 docs(changelog): correct claims the code does not support
An audit of the v1.13.0 notes against the implementation found several
claims that are wrong or overstated. These are user-facing release notes,
so a false claim changes what people believe about their own security.

Corrected:

- **Privacy Lock.** The notes said it gates Messages "behind a password or
  biometric" on "Android and Desktop", with inactivity auto-lock and
  optional preview redaction. It does not exist on Android at all — no
  code, no settings entry — and there is no biometric implementation on
  any platform (`CredentialPrompter` has no implementations). Preview
  redaction persists a setting nothing reads, and the auto-lock is a fixed
  timer, since the idle-reset modifier is never applied. It is also a
  screen gate rather than encryption at rest: the account stays live and
  messages keep syncing while locked. Now described as what ships — a
  password gate on the Desktop Messages and Wallet columns.
- **"Every signature, payment, or data read needs your explicit
  approval."** Only payments require per-use consent; everything else can
  be granted once and reused, and the default trust level auto-signs notes,
  reactions and encryption after a single tap.
- **Web of Trust (GrapeRank)** was listed under app features, but crawling
  and scoring exist only in the `amy` CLI — no app module references it.
  The app consumes NIP-85 cards published by an operator, which is what the
  entry now says.
- **"One-tap trust for your follows' relays"** described bulk-granting
  relays used by people you follow. What exists is category rules evaluated
  per challenge.
- **Pinned web apps "show the app's own icon"** — they render a generic
  placeholder.
- **PoW** contradicted itself: "all cores" in one entry, "half the device's
  cores" in another. The latter matches `PoWPolicy.minerWorkers`.
- **Onion-Location "through every HTTP client"** — the Android app's
  clients only; the desktop, CLI, geode and sandbox blob clients don't
  install it.
- **Relay hardening** claimed REQ refusals stop immediately and failures
  evict "on the first strike"; both take repeated failures, and the
  first-strike eviction applies only to crawls.
- **`bunker://` links cannot be pasted in** — Amethyst only emits them.
- **Git code browser** needs a repository with an http(s) clone URL.
- **Geohash anonymous identity** — the per-area identity is unlinkable, but
  the optional nickname is one global handle, so setting it links your
  posts across areas.
- **Concord ban** is read-time enforcement for everyone else; the banned
  member keeps the keys until a Refounding.

Adds an Upgrading section for the user-visible effects of the per-account
isolation work: sites signed out once, permissions re-asked per account,
and relay logins now prompting under the default remote-signer policy.

Contributor and translator credits are left as they are — several entries
are unresolved npubs and Crowdin-generated usernames, but correcting
attribution is not a call to make from the code.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 19:52:52 -04:00
Claude fb730d7ea8 docs(changelog): add v1.13.0 release notes
Add docs/changelog/v1.13.00.md — the v1.13.0 "Web Apps, Communities & Git"
release notes — and link it at the top of the changelog index.

Covers the full v1.12.6..HEAD range: the in-app Browser and NIP-5D/5A web
clients, Concord communities, NIP-34 git collaboration, Location Channels, the
NIP-46 Remote Signer and Privacy Lock, NIP-42 relay login permissions, NIP-29
Groups, the redesigned Messages inbox, Blossom file sync, Podcasting 2.0,
proof-of-work (NIP-13), negentropy (NIP-77), GrapeRank, accent theming, and the
Desktop, amy CLI, Quartz, and Geode work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JrPNt4FchqArpMtAHHfqGi
2026-07-19 19:06:03 +00:00
vitorpamplonaandgithub-actions[bot] 9fb3603ed6 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-18 14:24:00 +00:00
vitorpamplonaandgithub-actions[bot] cbcb8d42ba chore: sync Crowdin translations and seed translator npub placeholders 2026-07-17 21:49:24 +00:00
davotoulaandgithub-actions[bot] f28b6381e3 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-17 19:26:39 +00:00
davotoulaandgithub-actions[bot] c7d4a72a34 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-17 07:57:11 +00:00
vitorpamplonaandgithub-actions[bot] 9d0cc8ad56 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-16 13:40:26 +00:00
vitorpamplonaandgithub-actions[bot] 01fbc34e5e chore: sync Crowdin translations and seed translator npub placeholders 2026-07-16 01:32:20 +00:00
vitorpamplonaandgithub-actions[bot] 65d16d185b chore: sync Crowdin translations and seed translator npub placeholders 2026-07-15 21:25:50 +00:00
vitorpamplonaandgithub-actions[bot] 6139923561 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-15 20:10:21 +00:00
Vitor PamplonaandGitHub f59b1e5c52 Merge pull request #3567 from vitorpamplona/l10n_crowdin_translations
New Crowdin Translations
2026-07-15 09:20:12 -04:00
vitorpamplonaandgithub-actions[bot] 46f0a84f80 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-15 12:16:57 +00:00
nrobi144andClaude Opus 4.8 3f56d177d8 feat(desktop): note scheduling + NIP-37 opt-in draft sync
Adds note scheduling and NIP-37 opt-in encrypted draft sync to Amethyst
Desktop, and extracts the existing Android scheduled-post code into
`commons` so both platforms (and PowJobRestorer) share one implementation.

- Compose → clock icon → date/time picker (presets + exact-minute); the
  note is pre-signed and stored locally, then published at its time.
- Publishes while the app is open (45s in-app tick + launch catch-up) AND
  while fully closed: an OS job (launchd / schtasks / systemd, registered
  only while the queue is non-empty) relaunches the binary in a headless,
  key-free `--publish-scheduled` mode that opens a websocket and pushes the
  pre-signed bytes.
- A "Scheduled" deck destination (tabs Scheduled / Drafts / Articles):
  status, cancel, publish-now, edit (cancel + reopen prefilled).
- Drafts: save-as-draft with a default-OFF "Sync across devices
  (encrypted)" toggle publishing a NIP-37 DraftWrapEvent (kind 31234,
  NIP-44 to self); drafts sync down on a fresh device.

Extraction / de-dup: ScheduledPost → commons/commonMain; ScheduledPostStore
+ ScheduledPostPublisher → commons/jvmAndroid (Jackson/java.io.File are
gate-forbidden in commonMain). The commons store is a strict superset of
upstream's parallel Android store (account-scoped claim, CLAIM_TTL crash
recovery, PUBLISHING-only status guards, reload-before-claim); upstream's
new ScheduledPostWorkGate gating is adopted to drive it. Single-writer file
lock + reload-before-claim so the in-app timer and headless process never
double-publish. Store file 0600, dir 0700.

macOS verified on the packaged app-image (compose+schedule, in-app publish,
app-closed launchd firing, Scheduled screen, NIP-37 draft round-trip).
Windows/Linux OS-integration authored but untested; headless has no Tor
routing yet — both documented in the PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 11:26:12 +03:00
davotoulaandgithub-actions[bot] 8e46714fca chore: sync Crowdin translations and seed translator npub placeholders 2026-07-12 20:06:46 +00:00
vitorpamplonaandgithub-actions[bot] 757f348a46 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-11 15:04:41 +00:00
davotoulaandgithub-actions[bot] 2edb22783c chore: sync Crowdin translations and seed translator npub placeholders 2026-07-11 14:32:20 +00:00
davotoulaandgithub-actions[bot] cc275ce62d chore: sync Crowdin translations and seed translator npub placeholders 2026-07-11 10:47:38 +00:00
vitorpamplonaandgithub-actions[bot] 835f1e5e85 chore: sync Crowdin translations and seed translator npub placeholders 2026-07-11 03:16:54 +00:00