diff --git a/.gitignore b/.gitignore
index e4c1d3c863..04f839856f 100755
--- a/.gitignore
+++ b/.gitignore
@@ -75,3 +75,6 @@ tmp/
planning/
.claude
+
+infra/sftp/
+!infra/sftp/upload/.gitkeep
\ No newline at end of file
diff --git a/README.md b/README.md
index 2f9b52c320..7e198749bd 100755
--- a/README.md
+++ b/README.md
@@ -26,28 +26,85 @@ At the moment, PubPub isn't particularly well setup for outside contributors. Ho
User-facing documentation is a work in progress, and can be found at https://help.pubpub.org. If you're interested in helping contribute to documentation, we'd love to hear from you. Please send a note to [hello@pubpub.org](mailto:hello@pubpub.org?subject=Documentation%20Contribution) introducing yourself and describing how you'd like to contribute.
-## To Install
-
-```
+## Local Development
+
+### Prerequisites
+
+- **Docker & Docker Compose**
+- **pnpm** (see `packageManager` in `package.json`; `corepack enable` to activate)
+- **SOPS + age** for decrypting environment secrets:
+ ```
+ brew install sops age
+ ```
+- **Age key** — ask a team member for the private key, then place it at:
+ ```
+ ~/.config/sops/age/keys.txt
+ ```
+- **pandoc + poppler** (macOS):
+ ```
+ brew install pandoc poppler
+ ```
+ (See `Aptfile` for equivalent Debian packages)
+
+### Quick Start
+
+PubPub requires [KF Auth](https://github.com/knowledgefutures/kf-auth-v3) for authentication. Start it first:
+
+```bash
+# Terminal 1: Start KF Auth
+cd ../kf-auth-v3
pnpm install
+pnpm dev
+```
+```bash
+# Terminal 2: Start PubPub
+pnpm install
+pnpm dev
```
-To run locally on a Mac, use [Homebrew](https://brew.sh/) to install a handful of dependencies:
+On first run, `pnpm dev` will:
+1. Auto-decrypt `infra/.env.local.enc` into `infra/.env.local` (requires age key)
+2. Start PostgreSQL, RabbitMQ, app server, and worker via Docker
+3. Seed a demo community if the database is empty
-```
-brew install pandoc poppler
-```
+Navigate to `http://localhost:9876`
-(See `Aptfile` for a list of equivalent Debian packages to install)
+### Default Credentials
-## To Run Dev Mode
+Sign in at http://localhost:9876 using the KF Auth seed admin account:
+- **Email:** `admin@kfauth.org`
+- **Password:** `admin123`
-```
-pnpm dev
+### Useful URLs
+
+| URL | Service |
+|-----|---------|
+| http://localhost:9876 | PubPub |
+| http://localhost:3000 | KF Auth (sign-in) |
+| http://localhost:3001 | KF Auth account dashboard |
+| http://localhost:9999 | Inbucket (email inbox for dev) |
+
+### Secrets
+
+Environment secrets are encrypted with SOPS + age. Decryption happens automatically
+on `pnpm dev`, but you can also run manually:
+
+```bash
+pnpm secrets:decrypt:local # infra/.env.local.enc → infra/.env.local
+pnpm secrets:encrypt:local # infra/.env.local → infra/.env.local.enc (after changes)
```
-Navigate to `localhost:9876`
+### Troubleshooting
+
+**`env file infra/.env.local not found`**
+Auto-decrypt may have failed. Ensure your age key is at `~/.config/sops/age/keys.txt`, then run `pnpm secrets:decrypt:local`.
+
+**Every page returns 404**
+The demo community may not have been seeded. Check Docker logs for `[seed]` messages. To reset from scratch: `docker compose -f infra/docker-compose.dev.yml down -v && pnpm dev`
+
+**Cannot log in / redirect loop**
+Make sure KF Auth is running on port 3000. PubPub expects the OIDC issuer at `http://localhost:3000`.
## Fonts
diff --git a/client/containers/DashboardSettings/CollectionSettings/CollectionSettings.tsx b/client/containers/DashboardSettings/CollectionSettings/CollectionSettings.tsx
index cb057e9b6f..5d6b6c8335 100644
--- a/client/containers/DashboardSettings/CollectionSettings/CollectionSettings.tsx
+++ b/client/containers/DashboardSettings/CollectionSettings/CollectionSettings.tsx
@@ -15,16 +15,35 @@ import CommunityOrCollectionLevelPubSettings from '../CommunitySettings/Communit
import DashboardSettingsFrame, { type Subtab } from '../DashboardSettingsFrame';
import CollectionDetailsEditor from './CollectionDetailsEditor';
import CollectionMetadataEditor from './CollectionMetadataEditor';
+import { ExportCollectionButton } from './ExportCollectionButton';
+
+type PastExport = {
+ id: string;
+ createdAt: string;
+ isProcessing: boolean;
+ output: { downloadUrl: string; ftpUploaded: boolean } | null;
+ error: string | null;
+};
+
+type FtpTargetOption = {
+ id: string;
+ host: string;
+ name: string | null;
+ filePath: string | null;
+ ftpType: string;
+};
type Props = {
settingsData: {
depositTarget?: DepositTarget | null;
+ collectionExports?: PastExport[] | null;
+ ftpTargets?: FtpTargetOption[] | null;
};
};
const CollectionSettings = (props: Props) => {
const {
- settingsData: { depositTarget },
+ settingsData: { depositTarget, collectionExports, ftpTargets },
} = props;
const {
communityData,
@@ -131,6 +150,31 @@ const CollectionSettings = (props: Props) => {
/>,
],
},
+ {
+ id: 'export',
+ title: 'Export',
+ icon: 'export',
+ hideSaveButton: true,
+ sections: [
+
+
+ Creates a zip export for the collection containing the PDF and JATS files
+ for each Pub in the collection.
+ {ftpTargets && (
+
+ {` `}If an FTP target is selected, the zip file will be uploaded to
+ that target.
+
+ )}
+