Skip to content

Setting Up Remotes

Collaboration in Portuni is central mode: the central server is the only thing that ever holds Drive credentials, and it reaches Drive with a single Service Account. A local (single-machine) workspace cannot register or route to a remote at all — see Data Modes. There is no per-user Google sign-in for Drive; only an admin or agent with access to the central server’s MCP tools configures a remote.

This guide walks the Service-Account path from “I have a fresh Google account” to “Portuni is pushing files to a Shared Drive on my behalf.” Most of the work is one-time admin in the Google Cloud Console; the Portuni-side configuration is two MCP calls. If you’d rather be walked through it interactively, run the setup-drive-remote MCP prompt (in Claude Code, /mcp__portuni__setup-drive-remote) and an agent will guide you step by step and make the calls for you.

  • A Service Account (SA) with a JSON key, stored on the central server.
  • One or more Google Shared Drives that the SA is a member of.
  • A registered Portuni remote pointing at each Shared Drive.
  • Routing rules that send each node type to the right remote.

Shared Drives only — service accounts have no My Drive quota, so a personal My Drive target is not supported.

One-time admin setup (per Portuni deployment)

Section titled “One-time admin setup (per Portuni deployment)”
  1. Go to https://console.cloud.google.com.
  2. Create a new project. Pick any name – this is internal scaffolding. (“Portuni” is fine.)
  3. With the project selected, enable the Google Drive API:
    • Search for “Google Drive API” in the API library.
    • Click “Enable.”
  1. Cloud Console -> IAM & Admin -> Service Accounts.
  2. “Create Service Account.”
  3. Name it something memorable like portuni-sync. The full email becomes portuni-sync@<project>.iam.gserviceaccount.com – note this address; you’ll need it next.
  4. Skip the optional “grant access” steps. Portuni only needs the SA’s identity, not project-level permissions.
  1. Click into the new Service Account -> “Keys” tab -> “Add Key” -> “Create new key” -> JSON.
  2. Download the JSON file.
  3. Keep it somewhere safe but accessible – you’ll paste its contents into portuni_setup_remote shortly. Do NOT commit it to a git repo; treat it like a password.

For every Shared Drive Portuni will manage:

  1. Open the Shared Drive in Google Drive.
  2. “Manage members.”
  3. Add the SA’s email (portuni-sync@<project>.iam.gserviceaccount.com).
  4. Role: Content Manager (typically). Higher roles work; “Viewer” or “Commenter” don’t.

You’ll need the Shared Drive’s ID for each one. To get it:

  • Open the Shared Drive in your browser.
  • The URL looks like https://drive.google.com/drive/folders/0AXyz... – the long ID after /folders/ is the Shared Drive ID.

Optional: if you want Portuni to use a specific subfolder inside the Shared Drive as its root (instead of the drive’s root), grab that folder’s ID the same way. This is the root_folder_id parameter below.

Once admin work is done, each Shared Drive becomes a Portuni remote:

portuni_setup_remote {
"name": "drive-workflow",
"type": "gdrive",
"config": {
"shared_drive_id": "0AXyz...",
"root_folder_id": "1Abc..." // optional
},
"service_account_json": "<paste the entire JSON file contents here>"
}

What happens:

  • The public configuration (name, type, shared_drive_id, root_folder_id) is written to the remotes table in Turso.
  • The SA JSON is stored via the configured TokenStore (file / keychain / varlock) – never in Turso.

Repeat for each Shared Drive you’re registering.

How the SA JSON is stored on each device is controlled by PORTUNI_TOKEN_STORE:

Store Where When to pick
file (default) $PORTUNI_WORKSPACE_ROOT/.portuni/tokens.json, mode 0600 Single-user laptop. Easy backup.
keychain OS keychain (macOS Keychain, libsecret, Windows Credential Manager) Hardened single-user device.
varlock Env vars populated by a password manager Team machines, CI, or 1Password / Bitwarden as source of truth

For the file store you don’t need to do anything – Portuni writes the JSON the first time portuni_setup_remote runs.

For keychain: set PORTUNI_TOKEN_STORE=keychain and re-run portuni_setup_remote to write through the keychain instead of a file.

For varlock: set PORTUNI_TOKEN_STORE=varlock plus PORTUNI_VARLOCK_WRITE_PROGRAM and PORTUNI_VARLOCK_WRITE_ARGS to teach Portuni how to write into your password manager. A typical 1Password setup:

op run -- env \
PORTUNI_TOKEN_STORE=varlock \
PORTUNI_VARLOCK_WRITE_PROGRAM=op \
PORTUNI_VARLOCK_WRITE_ARGS='item edit "portuni/{name}" {field}={value}' \
npm start

A registered remote does nothing until a routing rule sends nodes to it. portuni_set_routing_policy takes a single rules: array and replaces the entire routing table — every existing rule that isn’t in the new list is deleted. So write out the full intended policy in one call:

portuni_set_routing_policy {
rules: [
{ priority: 10, node_type: "process", org_slug: "workflow", remote_name: "drive-workflow" },
{ priority: 100, node_type: "process", org_slug: null, remote_name: "shared-processes" },
{ priority: 100, node_type: "project", org_slug: null, remote_name: "projects-hub" }
]
}

null on node_type or org_slug is a wildcard (“any”). Lower priority wins – use 10 for org-specific overrides and 100 for catch-all defaults. Tie-break is by insertion order within the rule list.

Verify with portuni_list_remotes – each remote shows the routing rules pointing at it.

Workflow’s setup ends up looking roughly like this:

Remote Holds
projects-hub All projects across all orgs
drive-workflow Workflow’s own processes, areas, principles
drive-tempo Tempo’s processes, areas, principles
drive-nautie Nautie’s processes, areas, principles

Routing rules:

project / * -> projects-hub / 100
process / workflow -> drive-workflow / 10
process / tempo -> drive-tempo / 10
process / nautie -> drive-nautie / 10
area / workflow -> drive-workflow / 10
area / tempo -> drive-tempo / 10
area / nautie -> drive-nautie / 10
principle / * -> drive-workflow / 100 (or wherever)

After this, portuni_store and portuni_pull “just work” – Portuni picks the right Shared Drive based on the node and pushes / pulls there.

The central server needs the SA JSON via its own TokenStore. There is only one server-side deployment to distribute it to, so this is a one-time setup rather than a per-device concern.

Workspace deployments with restricted Shared Drives

Section titled “Workspace deployments with restricted Shared Drives”

Some Google Workspace setups configure Shared Drives so external members – which the SA technically is – can’t be added. In that case the SA flow won’t work. Two workarounds:

  1. Remove the external-member restriction on the target drives (admin setting in Google Admin Console).
  2. Use domain-wide delegation (a Workspace user acting on behalf of others) so the SA acts as a real Workspace member instead of an external one. Portuni does not yet implement this end-to-end (the underlying sub-claim support in drive-sa-auth.ts is there, but nothing passes it yet) — see the design spec’s “Domain-wide delegation” section.
  • Delete on Drive is soft. portuni_delete_file defaults to mode: "complete" (removes the local file, the DB row, and the remote copy). On Drive remotes, the remote-side delete moves the file to Drive Trash rather than truly destroying it (30-day recovery window). The unregister_only mode drops just the DB row without touching disk or remote — useful when the file is already gone from both.
  • Rename of a node does NOT rename the Drive folder. Folder paths are anchored on the immutable sync_key. Use portuni_rename_folder for an explicit, atomic rename of the visible folder name.
  • Native files (Docs / Sheets / Slides) are tracked but not round-trip synced. They have URL + modified-at metadata. Use portuni_snapshot to export a PDF / markdown / docx copy as a regular tracked file.
  • One folder per path. Drive allows several sibling folders with the same name; Portuni never relies on that. Folder creation is single-flighted per path, and after every create the siblings are re-checked — an older same-name folder wins and the newly created one is trashed. Where duplicates already exist (created by earlier versions), the oldest folder is pinned for new content, files inside the other copies still resolve, and the server logs [portuni:drive] ... duplicate folders for "<path>" with the Drive IDs. Merge the listed folders into the oldest one by hand; Portuni does not move content between duplicates.
  • Drive versioning is the safety net. 30-day version history under Portuni’s audit log gives a second line of defense for anything overwritten.