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
8 changes: 8 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ jobs:
- name: Run tests
run: npm test

- name: Run canonical Bluesky archive contract tests
run: npm run test:archive-contract

- name: Check for a newer canonical Bluesky archive bundle
env:
GITHUB_TOKEN: ${{ github.token }}
run: python3 scripts/archive-contract/check_latest.py

build_ios:
name: Build iOS
runs-on: macos-26
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,11 @@ jobs:

- name: Run tests
run: npm test

- name: Run canonical Bluesky archive contract tests
run: npm run test:archive-contract

- name: Check for a newer canonical Bluesky archive bundle
env:
GITHUB_TOKEN: ${{ github.token }}
run: python3 scripts/archive-contract/check_latest.py
86 changes: 45 additions & 41 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,62 +9,66 @@ Mobile and desktop Cyd offer the same Bluesky account-management capabilities an
_Avoid_: UI parity, identical clients

**Cyd archive**:
A complete, self-contained, versioned snapshot of an account's saved data and media that supported Cyd clients can exchange without losing its meaning.
_Avoid_: HTML export, database backup
An export of one social-platform account's saved data. Each platform defines its own archive contents, behavior, compatibility guarantees, and version history, so references to a particular archive must name the platform.
_Avoid_: archive, platform-neutral archive

**Interchange database**:
The canonical representation of structured account data inside a Cyd archive. It is independent of each client's private runtime storage.
_Avoid_: runtime database, mobile database
**Cyd Bluesky archive**:
A Cyd archive governed by the Bluesky archive contract. Version 2 is the first supported Bluesky archive format; the earlier unversioned mobile prototype is not a compatibility surface.
_Avoid_: Cyd archive v2, platform-neutral v2 archive

**Bluesky interchange database**:
The canonical representation of structured Bluesky account data inside a Cyd Bluesky archive. It is independent of each client's private runtime storage.
_Avoid_: interchange database, runtime database, mobile database

**Bluesky identity**:
A Bluesky account identified durably by its DID, even when its handle changes or separate Cyd installations know it by different local identifiers.
_Avoid_: handle, Cyd UUID

**Local account**:
A client's local representation of a Bluesky identity, identified within Cyd by a UUID and containing that client's settings and saved data.
_Avoid_: Bluesky identity
**Bluesky local account**:
A client's local representation of a Bluesky identity, identified within Cyd by a UUID and containing that client's Bluesky account settings and Bluesky saved data.
_Avoid_: local account, Bluesky identity

**Archive import**:
An idempotent recovery merge of a Cyd archive into a matching local account, preserving the union of saved data while collapsing records that share stable Bluesky identifiers. It may restore data previously removed through local deletion.
_Avoid_: replace, synchronize
**Bluesky archive import**:
An idempotent recovery merge of a Cyd Bluesky archive into a matching Bluesky local account, preserving the union of Bluesky saved data while collapsing records that share stable Bluesky identifiers. It may restore data previously removed through Bluesky local deletion.
_Avoid_: archive import, replace, synchronize

**Account settings**:
Local preferences governing how Cyd saves and manages a Bluesky identity. An archive can supply defaults for a new local account, but does not silently override an existing local account's preferences or schedules.
_Avoid_: account data, archive state
**Bluesky account settings**:
Local preferences governing how Cyd saves and manages a Bluesky identity. A Cyd Bluesky archive can supply defaults for a new Bluesky local account, but does not silently override an existing Bluesky local account's preferences or schedules.
_Avoid_: account settings, account data, archive state

**Scheduled reminder**:
A prompt to review and start due account-management work. It does not authorize Cyd to perform deletion unattended; clients may use platform-appropriate delivery such as local notifications or server-scheduled push.
_Avoid_: scheduled job, automatic deletion
**Bluesky scheduled reminder**:
A prompt to review and start due Bluesky account-management work. It does not authorize Cyd to perform deletion unattended; clients may use platform-appropriate delivery such as local notifications or server-scheduled push.
_Avoid_: scheduled reminder, scheduled job, automatic deletion

**Connection**:
A local installation's authorization to act on a Bluesky identity. Connections are established separately on each client and are never part of a Cyd archive.
Disconnecting removes authorization without removing the local account or its saved data.
_Avoid_: account data, imported session, local account
**Bluesky connection**:
A local installation's authorization to act on a Bluesky identity. Bluesky connections are established separately on each client and are never part of a Cyd Bluesky archive.
Disconnecting removes authorization without removing the Bluesky local account or its Bluesky saved data.
_Avoid_: connection, account data, imported session, Bluesky local account

**Saved data**:
The account records and complete media Cyd has preserved locally, including material that may later disappear from Bluesky. When a record is selected for saving, its full media is part of the saved data regardless of the record category.
_Avoid_: live feed, HTML export
**Bluesky saved data**:
The Bluesky records and complete media Cyd has preserved locally, including material that may later disappear from Bluesky. When a record is selected for saving, its full media is part of the Bluesky saved data regardless of the record category.
_Avoid_: saved data, live feed, HTML export

**Saved record**:
**Bluesky saved record**:
The latest representation of a Bluesky record observed by Cyd at a stable AT URI, together with its observation timestamps and deletion state. It is not a history of every CID revision.
_Avoid_: record revision, live record
_Avoid_: saved record, record revision, live record

**Browse**:
Inspect saved data inside Cyd without requiring a Bluesky connection or network access.
_Avoid_: view on Bluesky, live feed
**Bluesky browse**:
Inspection of Bluesky saved data inside Cyd without requiring a Bluesky connection or network access.
_Avoid_: browse, view on Bluesky, live feed

**Context snapshot**:
The directly referenced author, reply parent, quoted record, external embed, and other captured information required to render a saved record faithfully without recursively preserving the surrounding social graph.
_Avoid_: full thread, live lookup
**Bluesky context snapshot**:
The directly referenced author, reply parent, quoted record, external embed, and other captured information required to render a Bluesky saved record faithfully without recursively preserving the surrounding social graph.
_Avoid_: context snapshot, full thread, live lookup

**Complete backup**:
A saved dataset or Cyd archive containing every expected asset for its selected records. Missing assets are explicit and make the backup incomplete without invalidating the data that was successfully preserved.
_Avoid_: valid archive, successful export
**Complete Bluesky backup**:
Bluesky saved data or a Cyd Bluesky archive containing every expected asset for its selected records. Missing assets are explicit and make the backup incomplete without invalidating the data that was successfully preserved.
_Avoid_: complete backup, valid archive, successful export

**Source deletion**:
Removal of a record or relationship from Bluesky while retaining Cyd's saved copy and deletion state.
_Avoid_: local deletion
**Bluesky source deletion**:
Removal of a record or relationship from Bluesky while retaining its Bluesky saved record and deletion state in Cyd.
_Avoid_: source deletion, Bluesky local deletion

**Local deletion**:
An explicit removal of saved data from Cyd, independent of whether the source still exists on Bluesky.
_Avoid_: source deletion, stop saving
**Bluesky local deletion**:
An explicit removal of Bluesky saved data from Cyd, independent of whether the source still exists on Bluesky.
_Avoid_: local deletion, Bluesky source deletion, stop saving
1 change: 0 additions & 1 deletion app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ export default ({ config }: ConfigContext): ExpoConfig => {
"@react-native-community/datetimepicker",
"expo-image",
"expo-localization",
"expo-sharing",
"expo-sqlite",
"expo-web-browser",
"./plugins/remove-android-permissions",
Expand Down
14 changes: 0 additions & 14 deletions app/account/[accountId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -211,19 +211,6 @@ export default function AccountScreen() {
router.replace("/");
}, [account, handleSignOut, router]);

const handleExportArchive = useCallback(async () => {
if (!account) {
throw new Error(
"Account information is not available yet. Please try again in a moment.",
);
}

await runWithAccountController(account, async (controller) => {
await controller.initAgent();
await controller.exportArchive();
});
}, [account]);

// Reset auth status when the account disappears (adjusting state during
// render, per React docs, instead of a synchronous setState in an effect).
const [previousAccountForAuth, setPreviousAccountForAuth] =
Expand Down Expand Up @@ -574,7 +561,6 @@ export default function AccountScreen() {
onReauthenticate={handleReauthenticate}
onSignOut={handleSignOut}
onRemoveAccount={handleRemoveAccount}
onExportArchive={handleExportArchive}
/>
</>
);
Expand Down
68 changes: 0 additions & 68 deletions app/account/components/AccountMenuSheet.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useCallback, useMemo, useState } from "react";
import {
ActivityIndicator,
Alert,
Linking,
Modal,
Expand All @@ -23,7 +22,6 @@ export type AccountMenuSheetProps = {
onSignOut?: () => Promise<void>;
onReauthenticate?: () => Promise<void>;
onRemoveAccount?: () => Promise<void>;
onExportArchive?: () => Promise<void>;
authStatus: "authenticated" | "signed_out" | "unknown";
};

Expand Down Expand Up @@ -56,11 +54,9 @@ export function AccountMenuSheet({
onSignOut,
onReauthenticate,
onRemoveAccount,
onExportArchive,
authStatus,
}: AccountMenuSheetProps) {
const [pendingAction, setPendingAction] = useState<string | null>(null);
const [isExporting, setIsExporting] = useState(false);
const [signInModalVisible, setSignInModalVisible] = useState(false);
const {
state: cydState,
Expand All @@ -74,12 +70,6 @@ export function AccountMenuSheet({
key: "bluesky-handle",
label: `Bluesky account: ${handle}`,
},
{
type: "action",
key: "export-archive",
label: "Export Bluesky archive",
log: `Export archive tapped for ${handle}`,
},
...(authStatus !== "authenticated"
? [
{
Expand Down Expand Up @@ -153,34 +143,6 @@ export function AccountMenuSheet({
return;
}

if (item.key === "export-archive") {
if (!onExportArchive) {
Alert.alert(
"Export unavailable",
"This action is not supported here.",
);
onClose();
return;
}

setPendingAction(item.key);
setIsExporting(true);
try {
await onExportArchive();
onClose();
} catch (err) {
const message =
err instanceof Error
? err.message
: "Unable to export archive right now.";
Alert.alert("Export failed", message);
} finally {
setIsExporting(false);
setPendingAction(null);
}
return;
}

setPendingAction(item.key);
if (item.key === "signout") {
if (!onSignOut) {
Expand Down Expand Up @@ -302,7 +264,6 @@ export function AccountMenuSheet({
},
[
onClose,
onExportArchive,
onReauthenticate,
onRemoveAccount,
onSignOut,
Expand Down Expand Up @@ -403,18 +364,6 @@ export function AccountMenuSheet({
})}
</View>
</View>
{isExporting && (
<View style={styles.exportingOverlay}>
<View
style={[styles.exportingModal, { backgroundColor: palette.card }]}
>
<ActivityIndicator size="large" color={palette.tint} />
<Text style={[styles.exportingText, { color: palette.text }]}>
Preparing archive...
</Text>
</View>
</View>
)}
</Modal>
<CydSignInModal
visible={signInModalVisible}
Expand Down Expand Up @@ -472,23 +421,6 @@ const styles = StyleSheet.create({
borderTopWidth: StyleSheet.hairlineWidth,
marginVertical: 4,
},
exportingOverlay: {
...StyleSheet.absoluteFill,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "center",
alignItems: "center",
},
exportingModal: {
borderRadius: 16,
padding: 32,
alignItems: "center",
gap: 16,
minWidth: 200,
},
exportingText: {
fontSize: 16,
fontWeight: "600",
},
});

export default AccountMenuSheet;
Loading