offscale in the catalogue, and the comments the rename falsified
The plugin half is in gitea.officer.dev/plugins/offscale (95b84ea). This is what
the platform owed it.
- marketplace: offscale now ships assets/icon.png, so the catalogue carries an
iconUrl and a `bare` tile like music's. The lucide `icon` stays as the fallback
for a failed asset publish — a glyph beats the generic box.
- Four schema files cited `headscale_servers` as the shape they copied. The table
is `offscale_servers` as of today, so those sentences named a table that no
longer exists. Same for secret-store.ts's list of purposes.
The crypto purpose moved with it: `decryptSecret('headscale', …)` in the plugin
is now `'offscale'`, and the secret-store row was renamed rather than abandoned,
so the key material is preserved and the change is reversible. Safe only because
offscale_servers held 0 rows — one stored API key and this would have been a
re-encryption, not a rename.
Verified: offscale_servers, uq_offscale_servers_one_active and
uq_offscale_servers_user_url all exist in Postgres and nothing named headscale
does. Dropped the empty table by hand first, because drizzle-kit push treats a
rename as an interactive prompt and would have hung.
tsgo clean. 808 pass / 7 fail — +21 from offscale's first tests, same 7 failures.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ import { users } from '../auth/schema';
|
|||||||
// instance was a shell task on the server rather than something the owner could do from the app.
|
// instance was a shell task on the server rather than something the owner could do from the app.
|
||||||
//
|
//
|
||||||
// It is a REGISTRY, not a single row: the owner adds any number of accounts and switches between them, the
|
// It is a REGISTRY, not a single row: the owner adds any number of accounts and switches between them, the
|
||||||
// same shape headscale_servers and photos_config use. Uniqueness is on the label rather than the URL,
|
// same shape offscale_servers and photos_config use. Uniqueness is on the label rather than the URL,
|
||||||
// because the same instance with a different company is a different account here, and two of those share
|
// because the same instance with a different company is a different account here, and two of those share
|
||||||
// a URL AND a token.
|
// a URL AND a token.
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { users } from '../auth/schema';
|
|||||||
//
|
//
|
||||||
// A REGISTRY rather than a single row, and here that is not speculative: this machine runs four Jellyfin
|
// A REGISTRY rather than a single row, and here that is not speculative: this machine runs four Jellyfin
|
||||||
// instances on separate ports (video, albums, DJ sets, and a second person's), and the owner switches
|
// instances on separate ports (video, albums, DJ sets, and a second person's), and the owner switches
|
||||||
// between them. Same shape as invoiceshelf_accounts and headscale_servers.
|
// between them. Same shape as invoiceshelf_accounts and offscale_servers.
|
||||||
//
|
//
|
||||||
// `accessToken` is encrypted at rest via ../crypto.ts. A Jellyfin access token can read every item, every
|
// `accessToken` is encrypted at rest via ../crypto.ts. A Jellyfin access token can read every item, every
|
||||||
// watch history and every stream on the instance, so a DB dump must not hand it over. Encryption is confined
|
// watch history and every stream on the instance, so a DB dump must not hand it over. Encryption is confined
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { users } from '../auth/schema';
|
|||||||
// than something the owner could do from the app.
|
// than something the owner could do from the app.
|
||||||
//
|
//
|
||||||
// It is a REGISTRY, not a single row: the owner adds any number of accounts and switches between them, the
|
// It is a REGISTRY, not a single row: the owner adds any number of accounts and switches between them, the
|
||||||
// same shape headscale_servers uses. Two accounts on the same instance is the normal case (one key per
|
// same shape offscale_servers uses. Two accounts on the same instance is the normal case (one key per
|
||||||
// Immich user), which is why the uniqueness below is on the label and not on the URL.
|
// Immich user), which is why the uniqueness below is on the label and not on the URL.
|
||||||
//
|
//
|
||||||
// `api_key` is encrypted at rest via ../crypto.ts. An Immich key can read and delete the entire library, so a
|
// `api_key` is encrypted at rest via ../crypto.ts. An Immich key can read and delete the entire library, so a
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ function readActive(purpose: string): string | null {
|
|||||||
/**
|
/**
|
||||||
* The active key for a purpose, created on first use.
|
* The active key for a purpose, created on first use.
|
||||||
*
|
*
|
||||||
* Purposes are plain strings and belong to whoever owns the data they protect: `jwt`, `headscale`,
|
* Purposes are plain strings and belong to whoever owns the data they protect: `jwt`, `offscale`,
|
||||||
* `wallet`, `photos`, `jellyfin`, `invoiceshelf`, `vault`, `service-connections`. A plugin asks for its
|
* `wallet`, `photos`, `jellyfin`, `invoiceshelf`, `vault`, `service-connections`. A plugin asks for its
|
||||||
* own and never another's — same rule `service_connections` rows already follow.
|
* own and never another's — same rule `service_connections` rows already follow.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { users } from '../auth/schema';
|
|||||||
// TWO COLUMNS HOLD SPENDING AUTHORITY AND THEY ARE PROTECTED DIFFERENTLY. This asymmetry is deliberate:
|
// TWO COLUMNS HOLD SPENDING AUTHORITY AND THEY ARE PROTECTED DIFFERENTLY. This asymmetry is deliberate:
|
||||||
//
|
//
|
||||||
// `config` — node credentials (macaroon, rune, LNDHub password, NWC URI). Encrypted at rest with
|
// `config` — node credentials (macaroon, rune, LNDHub password, NWC URI). Encrypted at rest with
|
||||||
// the 'wallet' store key via ../crypto.ts, the way headscale_servers.api_key uses its
|
// the 'wallet' store key via ../crypto.ts, the way offscale_servers.api_key uses its
|
||||||
// own. It CANNOT be
|
// own. It CANNOT be
|
||||||
// passphrase-protected: background balance polling needs it without the owner present.
|
// passphrase-protected: background balance polling needs it without the owner present.
|
||||||
//
|
//
|
||||||
@@ -58,7 +58,7 @@ export const walletWallets = pgTable(
|
|||||||
(t) => [
|
(t) => [
|
||||||
uniqueIndex('uq_wallet_wallets_user_name').on(t.userId, t.name),
|
uniqueIndex('uq_wallet_wallets_user_name').on(t.userId, t.name),
|
||||||
// At most one active wallet per owner, enforced by the DB rather than convention — a partial unique
|
// At most one active wallet per owner, enforced by the DB rather than convention — a partial unique
|
||||||
// index over active rows only, mirroring uq_headscale_servers_one_active.
|
// index over active rows only, mirroring uq_offscale_servers_one_active.
|
||||||
uniqueIndex('uq_wallet_wallets_one_active')
|
uniqueIndex('uq_wallet_wallets_one_active')
|
||||||
.on(t.userId)
|
.on(t.userId)
|
||||||
.where(sql`${t.isActive}`),
|
.where(sql`${t.isActive}`),
|
||||||
|
|||||||
@@ -124,6 +124,10 @@ const CATALOGUE: CatalogueEntry[] = [
|
|||||||
label: 'Offscale',
|
label: 'Offscale',
|
||||||
summary: 'Your tailnet — machines, users, pre-auth keys, access policy and device invites',
|
summary: 'Your tailnet — machines, users, pre-auth keys, access policy and device invites',
|
||||||
color: '#818cf8',
|
color: '#818cf8',
|
||||||
|
// Ships artwork as of 2026-08-15, so the tile is `bare` like music's. `icon` stays as the fallback
|
||||||
|
// for the case the assets fail to publish — the glyph is strictly better than the generic box.
|
||||||
|
tile: 'bare',
|
||||||
|
iconUrl: 'https://gitea.officer.dev/plugins/offscale/raw/branch/main/assets/icon.png',
|
||||||
icon: 'Network',
|
icon: 'Network',
|
||||||
source: 'https://gitea.officer.dev/plugins/offscale.git',
|
source: 'https://gitea.officer.dev/plugins/offscale.git',
|
||||||
permissions: [
|
permissions: [
|
||||||
|
|||||||
Reference in New Issue
Block a user