@fusebase/fusebase-gate-sdk 2.11.6 → 2.11.8-sdk.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@
5
5
  * Domain: files
6
6
  */
7
7
  import type { Client } from "../runtime/transport";
8
- import type { CompleteMultipartFileUploadRequestContract, CompleteMultipartFileUploadResponseContract, DeleteFileRequestContract, DeleteFileResponseContract, MultipartUploadIdInPathRequired, orgIdInPathRequired, StartMultipartFileUploadRequestContract, StartMultipartFileUploadResponseContract } from "../types";
8
+ import type { BucketAttachmentContract, BucketAttachmentGlobalIdInPathRequired, CompleteMultipartFileUploadRequestContract, CompleteMultipartFileUploadResponseContract, CreateBucketAttachmentRequestContract, CreateTempStoredFileUploadRequestContract, CreateTempStoredFileUploadResponseContract, DeleteFileRequestContract, DeleteFileResponseContract, ListBucketAttachmentsResponseContract, MultipartUploadIdInPathRequired, orgIdInPathRequired, StartMultipartFileUploadRequestContract, StartMultipartFileUploadResponseContract, UpdateBucketAttachmentRequestContract } from "../types";
9
9
  export declare class FilesApi {
10
10
  private client;
11
11
  constructor(client: Client);
@@ -21,6 +21,28 @@ export declare class FilesApi {
21
21
  headers?: Record<string, string>;
22
22
  body: CompleteMultipartFileUploadRequestContract;
23
23
  }): Promise<CompleteMultipartFileUploadResponseContract>;
24
+ /**
25
+ * Create bucket attachment
26
+ * Turns an uploaded temp stored file into a bucket-service attachment in the organization's `app` bucket, so it shows up in the file listing next to files from notes and portals. Only app tokens can call it: the attachment is owned by the app the token was issued for.
27
+ */
28
+ createBucketAttachment(params: {
29
+ path: {
30
+ orgId: orgIdInPathRequired;
31
+ };
32
+ headers?: Record<string, string>;
33
+ body: CreateBucketAttachmentRequestContract;
34
+ }): Promise<BucketAttachmentContract>;
35
+ /**
36
+ * Create presigned file upload URL
37
+ * Returns a presigned URL for uploading a whole file to file-service in one PUT. The client sends only the content-type header returned in `headers` plus the bytes, then passes `tempStoredFileName` on to the operation that creates the record (for example createBucketAttachment). Use startMultipartFileUpload instead for files large enough to need parts.
38
+ */
39
+ createTempStoredFileUpload(params: {
40
+ path: {
41
+ orgId: orgIdInPathRequired;
42
+ };
43
+ headers?: Record<string, string>;
44
+ body: CreateTempStoredFileUploadRequestContract;
45
+ }): Promise<CreateTempStoredFileUploadResponseContract>;
24
46
  /**
25
47
  * Delete file
26
48
  * Deletes a file-service stored file by stored-file UUID. Gate never handles the file bytes.
@@ -32,6 +54,25 @@ export declare class FilesApi {
32
54
  headers?: Record<string, string>;
33
55
  body: DeleteFileRequestContract;
34
56
  }): Promise<DeleteFileResponseContract>;
57
+ /**
58
+ * List bucket attachments
59
+ * Lists the organization's files from every source in one list: files uploaded into an app (`app`), note attachments (`note`) and portal files (`portalPage`, `portalGlobal`, `portalFiles`, `portalBlock`). Each item carries its source `target`, the uploader `userId` (resolve the email and role with the org-users operations), the `size` in bytes, the custom `attributes` and the `accessPrincipals`. Filter by source with `targets`, drop small files with `sizeFrom`, and match custom metadata with `attributes`, a JSON object of strings sent as a string, such as `{"source":"file-manager"}`, where every given pair must match exactly (`total` follows the same filter). Clients only receive files their access principals or the bucket permissions allow.
60
+ */
61
+ listBucketAttachments(params: {
62
+ path: {
63
+ orgId: orgIdInPathRequired;
64
+ };
65
+ query?: {
66
+ targets?: unknown[];
67
+ sizeFrom?: number;
68
+ attributes?: string;
69
+ limit?: number;
70
+ offset?: number;
71
+ sortField?: string;
72
+ sortOrder?: string;
73
+ };
74
+ headers?: Record<string, string>;
75
+ }): Promise<ListBucketAttachmentsResponseContract>;
35
76
  /**
36
77
  * Start multipart file upload
37
78
  * Starts a file-service multipart upload and returns direct PUT URLs. Clients upload each part to the returned URLs, keep each response ETag, and pass those ETags to completeMultipartFileUpload.
@@ -43,4 +84,16 @@ export declare class FilesApi {
43
84
  headers?: Record<string, string>;
44
85
  body: StartMultipartFileUploadRequestContract;
45
86
  }): Promise<StartMultipartFileUploadResponseContract>;
87
+ /**
88
+ * Update bucket attachment
89
+ * Renames a bucket attachment or replaces its attributes and accessPrincipals. Send `accessPrincipals: null` to drop every restriction and show the file to everyone who can see the organization bucket.
90
+ */
91
+ updateBucketAttachment(params: {
92
+ path: {
93
+ orgId: orgIdInPathRequired;
94
+ globalId: BucketAttachmentGlobalIdInPathRequired;
95
+ };
96
+ headers?: Record<string, string>;
97
+ body: UpdateBucketAttachmentRequestContract;
98
+ }): Promise<BucketAttachmentContract>;
46
99
  }
@@ -26,6 +26,36 @@ class FilesApi {
26
26
  expectedContentType: "application/json",
27
27
  });
28
28
  }
29
+ /**
30
+ * Create bucket attachment
31
+ * Turns an uploaded temp stored file into a bucket-service attachment in the organization's `app` bucket, so it shows up in the file listing next to files from notes and portals. Only app tokens can call it: the attachment is owned by the app the token was issued for.
32
+ */
33
+ async createBucketAttachment(params) {
34
+ return this.client.request({
35
+ method: "POST",
36
+ path: "/:orgId/bucket-attachments",
37
+ pathParams: params.path,
38
+ headers: params.headers,
39
+ body: params.body,
40
+ opId: "createBucketAttachment",
41
+ expectedContentType: "application/json",
42
+ });
43
+ }
44
+ /**
45
+ * Create presigned file upload URL
46
+ * Returns a presigned URL for uploading a whole file to file-service in one PUT. The client sends only the content-type header returned in `headers` plus the bytes, then passes `tempStoredFileName` on to the operation that creates the record (for example createBucketAttachment). Use startMultipartFileUpload instead for files large enough to need parts.
47
+ */
48
+ async createTempStoredFileUpload(params) {
49
+ return this.client.request({
50
+ method: "POST",
51
+ path: "/:orgId/files/uploads/presigned",
52
+ pathParams: params.path,
53
+ headers: params.headers,
54
+ body: params.body,
55
+ opId: "createTempStoredFileUpload",
56
+ expectedContentType: "application/json",
57
+ });
58
+ }
29
59
  /**
30
60
  * Delete file
31
61
  * Deletes a file-service stored file by stored-file UUID. Gate never handles the file bytes.
@@ -41,6 +71,21 @@ class FilesApi {
41
71
  expectedContentType: "application/json",
42
72
  });
43
73
  }
74
+ /**
75
+ * List bucket attachments
76
+ * Lists the organization's files from every source in one list: files uploaded into an app (`app`), note attachments (`note`) and portal files (`portalPage`, `portalGlobal`, `portalFiles`, `portalBlock`). Each item carries its source `target`, the uploader `userId` (resolve the email and role with the org-users operations), the `size` in bytes, the custom `attributes` and the `accessPrincipals`. Filter by source with `targets`, drop small files with `sizeFrom`, and match custom metadata with `attributes`, a JSON object of strings sent as a string, such as `{"source":"file-manager"}`, where every given pair must match exactly (`total` follows the same filter). Clients only receive files their access principals or the bucket permissions allow.
77
+ */
78
+ async listBucketAttachments(params) {
79
+ return this.client.request({
80
+ method: "GET",
81
+ path: "/:orgId/bucket-attachments",
82
+ pathParams: params.path,
83
+ query: params.query,
84
+ headers: params.headers,
85
+ opId: "listBucketAttachments",
86
+ expectedContentType: "application/json",
87
+ });
88
+ }
44
89
  /**
45
90
  * Start multipart file upload
46
91
  * Starts a file-service multipart upload and returns direct PUT URLs. Clients upload each part to the returned URLs, keep each response ETag, and pass those ETags to completeMultipartFileUpload.
@@ -56,5 +101,20 @@ class FilesApi {
56
101
  expectedContentType: "application/json",
57
102
  });
58
103
  }
104
+ /**
105
+ * Update bucket attachment
106
+ * Renames a bucket attachment or replaces its attributes and accessPrincipals. Send `accessPrincipals: null` to drop every restriction and show the file to everyone who can see the organization bucket.
107
+ */
108
+ async updateBucketAttachment(params) {
109
+ return this.client.request({
110
+ method: "PATCH",
111
+ path: "/:orgId/bucket-attachments/:globalId",
112
+ pathParams: params.path,
113
+ headers: params.headers,
114
+ body: params.body,
115
+ opId: "updateBucketAttachment",
116
+ expectedContentType: "application/json",
117
+ });
118
+ }
59
119
  }
60
120
  exports.FilesApi = FilesApi;
@@ -1,5 +1,6 @@
1
1
  export type FileIdInPathRequired = string;
2
2
  export type MultipartUploadIdInPathRequired = string;
3
+ export type BucketAttachmentGlobalIdInPathRequired = string;
3
4
  export type FileUploadMethodContract = "PUT";
4
5
  export interface StartMultipartFileUploadRequestContract {
5
6
  filename: string;
@@ -7,6 +8,95 @@ export interface StartMultipartFileUploadRequestContract {
7
8
  contentType?: string | null;
8
9
  folder?: string | null;
9
10
  }
11
+ export interface CreateTempStoredFileUploadRequestContract {
12
+ name: string;
13
+ type?: string | null;
14
+ size?: number | null;
15
+ folder?: string | null;
16
+ public?: boolean | null;
17
+ }
18
+ /**
19
+ * Who may see an app file. Null means everyone who can see the org bucket.
20
+ */
21
+ export interface BucketAttachmentAccessPrincipalsContract {
22
+ roles?: string[] | null;
23
+ userIds?: number[] | null;
24
+ /** Organization group global ids. */
25
+ groupIds?: string[] | null;
26
+ }
27
+ /**
28
+ * Custom key/value metadata set by the uploader (max 50 keys, key and value max
29
+ * 255 chars).
30
+ */
31
+ export type BucketAttachmentAttributesContract = Record<string, string>;
32
+ export interface CreateBucketAttachmentRequestContract {
33
+ /**
34
+ * Name returned by createTempStoredFileUpload or completeMultipartFileUpload.
35
+ */
36
+ tempStoredFileName: string;
37
+ attributes?: BucketAttachmentAttributesContract | null;
38
+ accessPrincipals?: BucketAttachmentAccessPrincipalsContract | null;
39
+ folder?: string | null;
40
+ }
41
+ export interface UpdateBucketAttachmentRequestContract {
42
+ filename?: string | null;
43
+ /**
44
+ * Replaces the whole attributes object when present.
45
+ */
46
+ attributes?: BucketAttachmentAttributesContract | null;
47
+ /**
48
+ * Null clears every principal, so the file goes back to everyone who can see
49
+ * the org bucket. Absent leaves the access principals untouched.
50
+ */
51
+ accessPrincipals?: BucketAttachmentAccessPrincipalsContract | null;
52
+ }
53
+ /**
54
+ * Bucket permissions of a note or portal file, as bucket-service stores them.
55
+ * Only returned to organization members, never to clients.
56
+ */
57
+ export interface BucketAttachmentPermissionsContract {
58
+ allowAll: boolean;
59
+ rejectAll: boolean;
60
+ allowUserIds: number[];
61
+ rejectUserIds: number[];
62
+ }
63
+ export interface BucketAttachmentContract {
64
+ globalId: string;
65
+ /**
66
+ * Source bucket of the file: `app` for files uploaded into the app itself,
67
+ * `note` or `portal` for files that came from elsewhere in the organization.
68
+ */
69
+ target: string;
70
+ targetId: string;
71
+ filename: string;
72
+ type: string;
73
+ size: number;
74
+ /**
75
+ * Fusebase user id of the uploader.
76
+ */
77
+ userId: number;
78
+ /**
79
+ * @format uri
80
+ */
81
+ url: string;
82
+ createdAt: number;
83
+ attributes: BucketAttachmentAttributesContract | null;
84
+ accessPrincipals: BucketAttachmentAccessPrincipalsContract | null;
85
+ /**
86
+ * Workspace the file came from, for note and portal files.
87
+ */
88
+ workspaceId?: string | null;
89
+ portalId?: string | null;
90
+ permissions?: BucketAttachmentPermissionsContract | null;
91
+ }
92
+ export interface ListBucketAttachmentsResponseContract {
93
+ items: BucketAttachmentContract[];
94
+ /**
95
+ * Number of files matching the filters, counted by bucket-service. Capped at
96
+ * 1000.
97
+ */
98
+ total: number;
99
+ }
10
100
  export interface DeleteFileRequestContract {
11
101
  fileId: string;
12
102
  }
@@ -40,6 +130,23 @@ export interface StartMultipartFileUploadResponseContract {
40
130
  tempStoredfileName: string;
41
131
  partNumber: number;
42
132
  }
133
+ export interface CreateTempStoredFileUploadResponseContract {
134
+ tempStoredFileName: string;
135
+ /**
136
+ * @format uri
137
+ */
138
+ uploadUrl: string;
139
+ method: FileUploadMethodContract;
140
+ /**
141
+ * Unix timestamp in seconds after which the upload URL stops working.
142
+ */
143
+ expiresAt: number;
144
+ /**
145
+ * The only headers the direct PUT may carry. The URL signs `host` alone, so
146
+ * sending anything beyond `content-type` breaks the signature.
147
+ */
148
+ headers: FileUploadHeadersContract;
149
+ }
43
150
  export interface CompleteMultipartFileUploadPartContract {
44
151
  etag: string;
45
152
  partNumber: number;
@@ -49,10 +156,26 @@ export interface CompleteMultipartFileUploadRequestContract {
49
156
  parts: CompleteMultipartFileUploadPartContract[];
50
157
  contentType?: string | null;
51
158
  folder?: string | null;
159
+ /**
160
+ * Create the stored-file record after finishing the upload. Defaults to true.
161
+ * Set to false to keep the result as a temp stored file only.
162
+ */
163
+ saveStoredFile?: boolean | null;
52
164
  }
53
165
  export interface CompleteMultipartFileUploadResponseContract {
54
- fileId: string;
55
- storedFileUUID: string;
166
+ /**
167
+ * Null when `saveStoredFile` was false.
168
+ */
169
+ fileId: string | null;
170
+ /**
171
+ * Null when `saveStoredFile` was false.
172
+ */
173
+ storedFileUUID: string | null;
174
+ /**
175
+ * Whether the stored-file record was created.
176
+ */
177
+ storedFileCreated: boolean;
178
+ tempStoredFileName: string;
56
179
  /**
57
180
  * File-service public object name returned by finish multipart upload.
58
181
  */
@@ -3,7 +3,7 @@ export interface OrgUrlResponseContract {
3
3
  orgId: string;
4
4
  /** Canonical HTTPS base URL for the organization (no trailing path). */
5
5
  url: string;
6
- /** Hostname used in `url` (custom CNAME domain or `{sub}.{fusebaseHost}`). */
6
+ /** Hostname used in `url` (custom CNAME domain or `{sub}.{fusebaseWebClientHost}`). */
7
7
  host: string;
8
8
  /** `cname` when the org uses a custom domain; otherwise `subdomain`. */
9
9
  kind: OrgUrlKindContract;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fusebase/fusebase-gate-sdk",
3
- "version": "2.11.6",
3
+ "version": "2.11.8-sdk.0",
4
4
  "description": "TypeScript SDK for Fusebase Gate APIs - Generated from contract introspection",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -0,0 +1,252 @@
1
+ # Release Notes 2.11.8-sdk.0
2
+
3
+ - Current ref: `HEAD`
4
+ - Previous tag: `v2.11.7-sdk.0`
5
+ - Generated at: 2026-09-21T07:25:49.110Z
6
+
7
+ ## Included Drafts
8
+
9
+ - `docs/release-notes/2026-06-08-get-org-url.md` - Get organization URL
10
+ - `docs/release-notes/2026-09-09-org-url-web-client-host.md` - getOrgUrl returns the web-client host
11
+ - `docs/release-notes/2026-09-14-bucket-attachments.md` - Bucket attachments for app files
12
+ - `docs/release-notes/2026-09-14-list-bucket-attachments.md` - One file list across app, note and portal files
13
+ - `docs/release-notes/2026-09-14-presigned-file-upload.md` - Presigned single-request file upload
14
+ - `docs/release-notes/2026-09-19-web-studio-motion-guidance.md` - Web Studio motion guidance
15
+
16
+ ## Summary
17
+
18
+ ### Get organization URL
19
+
20
+ Gate exposes `GET /:orgId/url` (`getOrgUrl`) to resolve the canonical HTTPS base URL for an organization. The hostname follows org-service rules: custom CNAME domain when configured, otherwise `{sub}.{FUSEBASE_WEB_CLIENT_HOST}` (see the 2026-09-09 note; before that it was `{sub}.{FUSEBASE_HOST}`).
21
+
22
+ ### getOrgUrl returns the web-client host
23
+
24
+ `getOrgUrl` built org subdomain URLs from `FUSEBASE_HOST` (the API host, e.g.
25
+ `thefusebase.com`), so it returned `{sub}.thefusebase.com`. Users know their org by
26
+ the web-client host (`{sub}.nimbusweb.me` on prod). Both hosts resolve, so the wrong
27
+ one never failed loudly.
28
+
29
+ `FUSEBASE_HOST` could not be repurposed — it also derives `app.thefusebase.com` for
30
+ the auth-form URLs.
31
+
32
+ ### Bucket attachments for app files
33
+
34
+ An app that uploaded a file through Gate had nowhere to put it: the stored file was
35
+ not part of the organization's file listing, so the File Manager app could only ever
36
+ show its own files. Gate now talks to bucket-service, so an uploaded file becomes an
37
+ attachment in the organization's `app` bucket and shows up next to files from notes
38
+ and portals.
39
+
40
+ ### One file list across app, note and portal files
41
+
42
+ The File Manager app could only show the files it uploaded itself. `listBucketAttachments`
43
+ returns the organization's files from every source in one list, with the metadata needed to
44
+ filter them (source, uploader, size) and to show each file only to people who may see it.
45
+
46
+ ### Presigned single-request file upload
47
+
48
+ Uploading a file through Gate always meant a multipart upload: start, PUT every part,
49
+ complete. For a file small enough to fit in one request that is three round trips for
50
+ no reason, and the completion step always created a stored-file record, which the
51
+ File Manager app does not want — it creates its own bucket attachment instead.
52
+
53
+ ### Web Studio motion guidance
54
+
55
+ Added the safe, declarative Web Studio motion contract to the Gate MCP prompt.
56
+
57
+
58
+ ## API / SDK Changes
59
+
60
+ ### Get organization URL
61
+
62
+ - Added `getOrgUrl` operation and `OrgsApi.getOrgUrl` SDK client method.
63
+ - Response fields: `url`, `host`, `kind` (`cname` | `subdomain`), `sub`, `customDomain`, `domainShorter`.
64
+ - Permission: `org.read` with org-scoped authz.
65
+
66
+ ### Bucket attachments for app files
67
+
68
+ - New op `createBucketAttachment`, `POST /:orgId/bucket-attachments` (`files.write`).
69
+ Body takes `tempStoredFileName` (required) plus optional `attributes`, `accessPrincipals`
70
+ and `folder`. Gate generates the attachment id and reads the owning app from the
71
+ token's `client` scope, so a call without an app token answers 400.
72
+ - New op `updateBucketAttachment`, `PATCH /:orgId/bucket-attachments/:globalId`
73
+ (`files.write`). Body takes `filename`, `attributes` and `accessPrincipals`, all optional.
74
+ A field that is not sent stays as it is; `accessPrincipals: null` clears every
75
+ restriction. bucket-service statuses (400, 404) are passed through unchanged.
76
+ - Both return the attachment as `globalId`, `target`, `targetId`, `filename`, `type`,
77
+ `size`, `userId`, `url`, `createdAt`, `attributes` and `accessPrincipals`.
78
+ - MCP `files` prompt covers both ops, and the Gate files skill reference is
79
+ regenerated.
80
+
81
+ ### One file list across app, note and portal files
82
+
83
+ - New op `listBucketAttachments`, `GET /:orgId/bucket-attachments` (`files.read`, clients
84
+ included). Query: `targets` (source buckets, for example `app` or `note`), `sizeFrom`
85
+ (bytes), `attributes`, `limit` (default 50, max 100), `offset`, `sortField`, `sortOrder`.
86
+ - `attributes` keeps only the files whose custom metadata contains every given pair, as a
87
+ JSON object of strings such as `{"source":"file-manager"}` (max 50 keys, 255 chars per key
88
+ and value). `total` follows the same filter. A malformed filter is a 400, answered before
89
+ bucket-service is called.
90
+ - Returns `{items, total}`. Each item carries `globalId`, `target` (the source), `targetId`,
91
+ `workspaceId`, `portalId`, `filename`, `type`, `size`, `userId` (the uploader), `url`,
92
+ `createdAt`, `attributes`, `accessPrincipals` and, for organization members, `permissions`.
93
+ - `createBucketAttachment` and `updateBucketAttachment` now return the same three extra
94
+ fields (`workspaceId`, `portalId`, `permissions`), null for app files.
95
+ - Gate resolves the caller's organization role and group memberships itself and forwards
96
+ them, with the caller's user id, to both the listing and the count. bucket-service then
97
+ decides what the caller may see. A client role is filtered in `client` permissions mode,
98
+ everyone else in `admin` mode.
99
+ - `accessPrincipals.groupIds` on `createBucketAttachment` and `updateBucketAttachment` are
100
+ organization group global ids (strings), the ids org-service and bucket-service use.
101
+ - MCP `files` prompt covers the new op, and the Gate files skill reference is regenerated.
102
+
103
+ ### Presigned single-request file upload
104
+
105
+ - New op `createTempStoredFileUpload`, `POST /:orgId/files/uploads/presigned`
106
+ (`files.write`). Body takes `name` (required) plus optional `type`, `size`,
107
+ `folder` and `public`. It returns `uploadUrl`, `method`, `expiresAt`,
108
+ `tempStoredFileName`, and `headers` holding the single `content-type` header the
109
+ PUT may carry — the URL signs `host` only, so anything else breaks the signature.
110
+ - `completeMultipartFileUpload` takes an optional `saveStoredFile` (default `true`).
111
+ With `false` it finishes the upload and skips the stored-file record.
112
+ - `completeMultipartFileUpload` response gains `storedFileCreated` and
113
+ `tempStoredfileName`. `fileId` and `storedFileUUID` are now nullable — they are
114
+ null exactly when `saveStoredFile` was `false`.
115
+ - MCP `files` prompt covers both, and the Gate files skill reference is regenerated.
116
+
117
+ ### Web Studio motion guidance
118
+
119
+ No operation or SDK contract changed. The `webStudio` MCP prompt is now version
120
+ `1.11.0` and describes the supported `reveal`, `count`, and `parallax` motion
121
+ attributes and their bounded options.
122
+
123
+
124
+ ## Consumer Impact
125
+
126
+ ### Get organization URL
127
+
128
+ - Apps and agents can stop hardcoding Fusebase hostnames when building org-scoped links.
129
+ - Portal domains and app magic-link hosts remain separate surfaces; use `listPortals` or app APIs for those.
130
+
131
+ ### getOrgUrl returns the web-client host
132
+
133
+ - Custom CNAME domains are unchanged — they already won over the host.
134
+ - Deployments without the new variable keep the previous behaviour via the fallback.
135
+
136
+ ### Bucket attachments for app files
137
+
138
+ - Nothing changes for existing callers: both ops are new and no existing response
139
+ shape moved.
140
+ - Deployments need `BUCKET_SERVICE_URL`. Without it the two new ops answer 500 and
141
+ every other op is unaffected.
142
+ - Gate calls bucket-service with the plain `Internal` header and no super secret, so
143
+ bucket-service authorizes the real user against the file's own organization. That
144
+ is what keeps an app from touching an attachment in another organization.
145
+
146
+ ### One file list across app, note and portal files
147
+
148
+ - Nothing changes for existing callers: the op is new and the two added fields are optional.
149
+ - A token issued for an app sees exactly what a user session for the same user sees: the
150
+ role and the group memberships are resolved from the organization membership and from
151
+ org-service, not from the request.
152
+ - `total` is counted by bucket-service, which counts the page it would return, so the gate
153
+ asks it for up to 1000 files. A larger organization reports 1000.
154
+
155
+ ### Presigned single-request file upload
156
+
157
+ - Existing callers are unaffected: `saveStoredFile` defaults to `true`, so the
158
+ response keeps the same values it had.
159
+ - TypeScript consumers reading `storedFileUUID` from the completion response now see
160
+ `string | null` and have to narrow it. Checking `storedFileCreated` is the clearest
161
+ way.
162
+
163
+ ### Web Studio motion guidance
164
+
165
+ MCP agents receive the same motion guidance as Studio Chat: they can request
166
+ compiler-owned motion without attempting to add site-authored JavaScript.
167
+
168
+
169
+ ## Verification
170
+
171
+ ### Get organization URL
172
+
173
+ - `npm test -- tests/unit/org-url-service.test.ts tests/unit/orgs-controller.test.ts`
174
+ - `npm run build:sdk`
175
+ - `npm run mcp:skills:generate`
176
+ - `npm run mcp:skills:validate`
177
+
178
+ ### getOrgUrl returns the web-client host
179
+
180
+ - `npm test -- tests/unit/config-env.test.ts tests/unit/orgs-controller.test.ts tests/unit/org-url-service.test.ts`
181
+
182
+ ### Bucket attachments for app files
183
+
184
+ - `npm test -- tests/unit/files`
185
+ - `npm run build`, `npm run build:sdk`, `npm run mcp:skills:generate`,
186
+ `npm run mcp:skills:validate`
187
+ - e2e-sdk and mcp-e2e cover the update flow and the 400/404 cases against a fake
188
+ bucket-service. Creating an attachment needs a real app token (`client` scope),
189
+ so that path is covered by unit tests and by QA on dev (NIM-44491).
190
+
191
+ ### One file list across app, note and portal files
192
+
193
+ - `npm test -- tests/unit/files tests/unit/bucket-service-client`
194
+ - `npm run build`, `npm run build:sdk`, `npm run mcp:skills:generate`,
195
+ `npm run mcp:skills:validate`
196
+ - e2e-sdk and mcp-e2e list app and note files against the fake bucket-service and check the
197
+ `targets` and `sizeFrom` filters. The fake enforces the caller access principals, and one
198
+ e2e-sdk case creates a real group, grants files by role and by group, and asserts both
199
+ a token and a user session see them while a file granted to neither stays hidden. Further
200
+ cases cover the `attributes` filter and its count, an app file addressed to another user
201
+ staying hidden from an org member, the uploader receiving their own restricted file, and a
202
+ client receiving the app file granted to the `client` role.
203
+
204
+ ### Presigned single-request file upload
205
+
206
+ - `npm test -- tests/unit/files`
207
+ - `npm run build`, `npm run build:sdk`, `npm run mcp:skills:generate`,
208
+ `npm run mcp:skills:validate`
209
+
210
+ ### Web Studio motion guidance
211
+
212
+ - `npm run build`
213
+ - `npm run mcp:skills:generate`
214
+ - `npm run mcp:skills:validate` remains blocked locally because the `skills_ref`
215
+ Python module is unavailable.
216
+
217
+
218
+ ## Follow-ups
219
+
220
+ ### Get organization URL
221
+
222
+ - None.
223
+
224
+ ### Bucket attachments for app files
225
+
226
+ - `listBucketAttachments` (NIM-44489) returns the aggregated listing these
227
+ attachments join.
228
+
229
+ ### Presigned single-request file upload
230
+
231
+ - `createBucketAttachment` (NIM-44488) is what turns `tempStoredFileName` into a
232
+ bucket attachment for the File Manager app.
233
+
234
+ ### Web Studio motion guidance
235
+
236
+ Deploy the matching Web Studio compiler release before relying on motion
237
+ attributes in published sites.
238
+
239
+
240
+ ## Changes
241
+
242
+ ### getOrgUrl returns the web-client host
243
+
244
+ - New env variable `FUSEBASE_WEB_CLIENT_HOST` (`config.fusebaseWebClientHost`),
245
+ falling back to `FUSEBASE_HOST` when unset.
246
+ - `getOrgUrl` now resolves subdomain org URLs against `fusebaseWebClientHost`, and so
247
+ do the markdown-note share URLs, which are built on the same org base.
248
+ - The MCP prompt host bag carries `fusebaseWebClientHost` alongside `fusebaseHost`;
249
+ the `getOrgUrl` prompt example uses the web-client one. `fusebaseHost` keeps meaning
250
+ the API/platform host everywhere.
251
+ - Added to nimbus-helm-chart `fusebase-gate` values and deployment env
252
+ (prod `nimbusweb.me`, dev `dev-thefusebase.com`).
@@ -1,9 +1,252 @@
1
- # Release Notes 2.11.6
1
+ # Release Notes 2.11.8-sdk.0
2
2
 
3
3
  - Current ref: `HEAD`
4
- - Previous tag: `v2.11.6`
5
- - Generated at: 2026-09-09T09:28:57.596Z
4
+ - Previous tag: `v2.11.7-sdk.0`
5
+ - Generated at: 2026-09-21T07:25:49.110Z
6
6
 
7
7
  ## Included Drafts
8
8
 
9
- - None
9
+ - `docs/release-notes/2026-06-08-get-org-url.md` - Get organization URL
10
+ - `docs/release-notes/2026-09-09-org-url-web-client-host.md` - getOrgUrl returns the web-client host
11
+ - `docs/release-notes/2026-09-14-bucket-attachments.md` - Bucket attachments for app files
12
+ - `docs/release-notes/2026-09-14-list-bucket-attachments.md` - One file list across app, note and portal files
13
+ - `docs/release-notes/2026-09-14-presigned-file-upload.md` - Presigned single-request file upload
14
+ - `docs/release-notes/2026-09-19-web-studio-motion-guidance.md` - Web Studio motion guidance
15
+
16
+ ## Summary
17
+
18
+ ### Get organization URL
19
+
20
+ Gate exposes `GET /:orgId/url` (`getOrgUrl`) to resolve the canonical HTTPS base URL for an organization. The hostname follows org-service rules: custom CNAME domain when configured, otherwise `{sub}.{FUSEBASE_WEB_CLIENT_HOST}` (see the 2026-09-09 note; before that it was `{sub}.{FUSEBASE_HOST}`).
21
+
22
+ ### getOrgUrl returns the web-client host
23
+
24
+ `getOrgUrl` built org subdomain URLs from `FUSEBASE_HOST` (the API host, e.g.
25
+ `thefusebase.com`), so it returned `{sub}.thefusebase.com`. Users know their org by
26
+ the web-client host (`{sub}.nimbusweb.me` on prod). Both hosts resolve, so the wrong
27
+ one never failed loudly.
28
+
29
+ `FUSEBASE_HOST` could not be repurposed — it also derives `app.thefusebase.com` for
30
+ the auth-form URLs.
31
+
32
+ ### Bucket attachments for app files
33
+
34
+ An app that uploaded a file through Gate had nowhere to put it: the stored file was
35
+ not part of the organization's file listing, so the File Manager app could only ever
36
+ show its own files. Gate now talks to bucket-service, so an uploaded file becomes an
37
+ attachment in the organization's `app` bucket and shows up next to files from notes
38
+ and portals.
39
+
40
+ ### One file list across app, note and portal files
41
+
42
+ The File Manager app could only show the files it uploaded itself. `listBucketAttachments`
43
+ returns the organization's files from every source in one list, with the metadata needed to
44
+ filter them (source, uploader, size) and to show each file only to people who may see it.
45
+
46
+ ### Presigned single-request file upload
47
+
48
+ Uploading a file through Gate always meant a multipart upload: start, PUT every part,
49
+ complete. For a file small enough to fit in one request that is three round trips for
50
+ no reason, and the completion step always created a stored-file record, which the
51
+ File Manager app does not want — it creates its own bucket attachment instead.
52
+
53
+ ### Web Studio motion guidance
54
+
55
+ Added the safe, declarative Web Studio motion contract to the Gate MCP prompt.
56
+
57
+
58
+ ## API / SDK Changes
59
+
60
+ ### Get organization URL
61
+
62
+ - Added `getOrgUrl` operation and `OrgsApi.getOrgUrl` SDK client method.
63
+ - Response fields: `url`, `host`, `kind` (`cname` | `subdomain`), `sub`, `customDomain`, `domainShorter`.
64
+ - Permission: `org.read` with org-scoped authz.
65
+
66
+ ### Bucket attachments for app files
67
+
68
+ - New op `createBucketAttachment`, `POST /:orgId/bucket-attachments` (`files.write`).
69
+ Body takes `tempStoredFileName` (required) plus optional `attributes`, `accessPrincipals`
70
+ and `folder`. Gate generates the attachment id and reads the owning app from the
71
+ token's `client` scope, so a call without an app token answers 400.
72
+ - New op `updateBucketAttachment`, `PATCH /:orgId/bucket-attachments/:globalId`
73
+ (`files.write`). Body takes `filename`, `attributes` and `accessPrincipals`, all optional.
74
+ A field that is not sent stays as it is; `accessPrincipals: null` clears every
75
+ restriction. bucket-service statuses (400, 404) are passed through unchanged.
76
+ - Both return the attachment as `globalId`, `target`, `targetId`, `filename`, `type`,
77
+ `size`, `userId`, `url`, `createdAt`, `attributes` and `accessPrincipals`.
78
+ - MCP `files` prompt covers both ops, and the Gate files skill reference is
79
+ regenerated.
80
+
81
+ ### One file list across app, note and portal files
82
+
83
+ - New op `listBucketAttachments`, `GET /:orgId/bucket-attachments` (`files.read`, clients
84
+ included). Query: `targets` (source buckets, for example `app` or `note`), `sizeFrom`
85
+ (bytes), `attributes`, `limit` (default 50, max 100), `offset`, `sortField`, `sortOrder`.
86
+ - `attributes` keeps only the files whose custom metadata contains every given pair, as a
87
+ JSON object of strings such as `{"source":"file-manager"}` (max 50 keys, 255 chars per key
88
+ and value). `total` follows the same filter. A malformed filter is a 400, answered before
89
+ bucket-service is called.
90
+ - Returns `{items, total}`. Each item carries `globalId`, `target` (the source), `targetId`,
91
+ `workspaceId`, `portalId`, `filename`, `type`, `size`, `userId` (the uploader), `url`,
92
+ `createdAt`, `attributes`, `accessPrincipals` and, for organization members, `permissions`.
93
+ - `createBucketAttachment` and `updateBucketAttachment` now return the same three extra
94
+ fields (`workspaceId`, `portalId`, `permissions`), null for app files.
95
+ - Gate resolves the caller's organization role and group memberships itself and forwards
96
+ them, with the caller's user id, to both the listing and the count. bucket-service then
97
+ decides what the caller may see. A client role is filtered in `client` permissions mode,
98
+ everyone else in `admin` mode.
99
+ - `accessPrincipals.groupIds` on `createBucketAttachment` and `updateBucketAttachment` are
100
+ organization group global ids (strings), the ids org-service and bucket-service use.
101
+ - MCP `files` prompt covers the new op, and the Gate files skill reference is regenerated.
102
+
103
+ ### Presigned single-request file upload
104
+
105
+ - New op `createTempStoredFileUpload`, `POST /:orgId/files/uploads/presigned`
106
+ (`files.write`). Body takes `name` (required) plus optional `type`, `size`,
107
+ `folder` and `public`. It returns `uploadUrl`, `method`, `expiresAt`,
108
+ `tempStoredFileName`, and `headers` holding the single `content-type` header the
109
+ PUT may carry — the URL signs `host` only, so anything else breaks the signature.
110
+ - `completeMultipartFileUpload` takes an optional `saveStoredFile` (default `true`).
111
+ With `false` it finishes the upload and skips the stored-file record.
112
+ - `completeMultipartFileUpload` response gains `storedFileCreated` and
113
+ `tempStoredfileName`. `fileId` and `storedFileUUID` are now nullable — they are
114
+ null exactly when `saveStoredFile` was `false`.
115
+ - MCP `files` prompt covers both, and the Gate files skill reference is regenerated.
116
+
117
+ ### Web Studio motion guidance
118
+
119
+ No operation or SDK contract changed. The `webStudio` MCP prompt is now version
120
+ `1.11.0` and describes the supported `reveal`, `count`, and `parallax` motion
121
+ attributes and their bounded options.
122
+
123
+
124
+ ## Consumer Impact
125
+
126
+ ### Get organization URL
127
+
128
+ - Apps and agents can stop hardcoding Fusebase hostnames when building org-scoped links.
129
+ - Portal domains and app magic-link hosts remain separate surfaces; use `listPortals` or app APIs for those.
130
+
131
+ ### getOrgUrl returns the web-client host
132
+
133
+ - Custom CNAME domains are unchanged — they already won over the host.
134
+ - Deployments without the new variable keep the previous behaviour via the fallback.
135
+
136
+ ### Bucket attachments for app files
137
+
138
+ - Nothing changes for existing callers: both ops are new and no existing response
139
+ shape moved.
140
+ - Deployments need `BUCKET_SERVICE_URL`. Without it the two new ops answer 500 and
141
+ every other op is unaffected.
142
+ - Gate calls bucket-service with the plain `Internal` header and no super secret, so
143
+ bucket-service authorizes the real user against the file's own organization. That
144
+ is what keeps an app from touching an attachment in another organization.
145
+
146
+ ### One file list across app, note and portal files
147
+
148
+ - Nothing changes for existing callers: the op is new and the two added fields are optional.
149
+ - A token issued for an app sees exactly what a user session for the same user sees: the
150
+ role and the group memberships are resolved from the organization membership and from
151
+ org-service, not from the request.
152
+ - `total` is counted by bucket-service, which counts the page it would return, so the gate
153
+ asks it for up to 1000 files. A larger organization reports 1000.
154
+
155
+ ### Presigned single-request file upload
156
+
157
+ - Existing callers are unaffected: `saveStoredFile` defaults to `true`, so the
158
+ response keeps the same values it had.
159
+ - TypeScript consumers reading `storedFileUUID` from the completion response now see
160
+ `string | null` and have to narrow it. Checking `storedFileCreated` is the clearest
161
+ way.
162
+
163
+ ### Web Studio motion guidance
164
+
165
+ MCP agents receive the same motion guidance as Studio Chat: they can request
166
+ compiler-owned motion without attempting to add site-authored JavaScript.
167
+
168
+
169
+ ## Verification
170
+
171
+ ### Get organization URL
172
+
173
+ - `npm test -- tests/unit/org-url-service.test.ts tests/unit/orgs-controller.test.ts`
174
+ - `npm run build:sdk`
175
+ - `npm run mcp:skills:generate`
176
+ - `npm run mcp:skills:validate`
177
+
178
+ ### getOrgUrl returns the web-client host
179
+
180
+ - `npm test -- tests/unit/config-env.test.ts tests/unit/orgs-controller.test.ts tests/unit/org-url-service.test.ts`
181
+
182
+ ### Bucket attachments for app files
183
+
184
+ - `npm test -- tests/unit/files`
185
+ - `npm run build`, `npm run build:sdk`, `npm run mcp:skills:generate`,
186
+ `npm run mcp:skills:validate`
187
+ - e2e-sdk and mcp-e2e cover the update flow and the 400/404 cases against a fake
188
+ bucket-service. Creating an attachment needs a real app token (`client` scope),
189
+ so that path is covered by unit tests and by QA on dev (NIM-44491).
190
+
191
+ ### One file list across app, note and portal files
192
+
193
+ - `npm test -- tests/unit/files tests/unit/bucket-service-client`
194
+ - `npm run build`, `npm run build:sdk`, `npm run mcp:skills:generate`,
195
+ `npm run mcp:skills:validate`
196
+ - e2e-sdk and mcp-e2e list app and note files against the fake bucket-service and check the
197
+ `targets` and `sizeFrom` filters. The fake enforces the caller access principals, and one
198
+ e2e-sdk case creates a real group, grants files by role and by group, and asserts both
199
+ a token and a user session see them while a file granted to neither stays hidden. Further
200
+ cases cover the `attributes` filter and its count, an app file addressed to another user
201
+ staying hidden from an org member, the uploader receiving their own restricted file, and a
202
+ client receiving the app file granted to the `client` role.
203
+
204
+ ### Presigned single-request file upload
205
+
206
+ - `npm test -- tests/unit/files`
207
+ - `npm run build`, `npm run build:sdk`, `npm run mcp:skills:generate`,
208
+ `npm run mcp:skills:validate`
209
+
210
+ ### Web Studio motion guidance
211
+
212
+ - `npm run build`
213
+ - `npm run mcp:skills:generate`
214
+ - `npm run mcp:skills:validate` remains blocked locally because the `skills_ref`
215
+ Python module is unavailable.
216
+
217
+
218
+ ## Follow-ups
219
+
220
+ ### Get organization URL
221
+
222
+ - None.
223
+
224
+ ### Bucket attachments for app files
225
+
226
+ - `listBucketAttachments` (NIM-44489) returns the aggregated listing these
227
+ attachments join.
228
+
229
+ ### Presigned single-request file upload
230
+
231
+ - `createBucketAttachment` (NIM-44488) is what turns `tempStoredFileName` into a
232
+ bucket attachment for the File Manager app.
233
+
234
+ ### Web Studio motion guidance
235
+
236
+ Deploy the matching Web Studio compiler release before relying on motion
237
+ attributes in published sites.
238
+
239
+
240
+ ## Changes
241
+
242
+ ### getOrgUrl returns the web-client host
243
+
244
+ - New env variable `FUSEBASE_WEB_CLIENT_HOST` (`config.fusebaseWebClientHost`),
245
+ falling back to `FUSEBASE_HOST` when unset.
246
+ - `getOrgUrl` now resolves subdomain org URLs against `fusebaseWebClientHost`, and so
247
+ do the markdown-note share URLs, which are built on the same org base.
248
+ - The MCP prompt host bag carries `fusebaseWebClientHost` alongside `fusebaseHost`;
249
+ the `getOrgUrl` prompt example uses the web-client one. `fusebaseHost` keeps meaning
250
+ the API/platform host everywhere.
251
+ - Added to nimbus-helm-chart `fusebase-gate` values and deployment env
252
+ (prod `nimbusweb.me`, dev `dev-thefusebase.com`).
@@ -1,61 +0,0 @@
1
- # Release Notes 2.11.6-sdk.0
2
-
3
- - Current ref: `HEAD`
4
- - Previous tag: `v2.11.6-sdk.0`
5
- - Generated at: 2026-09-08T12:29:57.106Z
6
-
7
- ## Included Drafts
8
-
9
- - `docs/release-notes/2026-09-07-app-auth-handoff.md` - Login handoff for the fusebase-auth ops
10
-
11
- ## Summary
12
-
13
- ### Login handoff for the fusebase-auth ops
14
-
15
- The four fusebase-auth operations that can end in an authenticated result now
16
- return an `appAuth` handoff beside `session`, so an app can obtain its own
17
- runtime token without holding a raw FuseBase session id.
18
-
19
-
20
- ## API / SDK Changes
21
-
22
- ### Login handoff for the fusebase-auth ops
23
-
24
- - Added `appAuth` (`exchangeToken`, `expiresInSeconds`, `authPath`) to the
25
- authenticated responses of `registerFusebaseUser`,
26
- `registerFusebaseOrgMember`, `loginFusebaseUser` and
27
- `completeFusebaseAuthChallenge`.
28
- - `authPath` is a relative URL on the app's own host, built from the
29
- `redirectPath` the app sent. Apps pass it through rather than assembling it.
30
- - Operation descriptions now point at the handoff instead of at
31
- `session.sessionId`.
32
- - `session` is deprecated. Gate omits it from all four responses when it runs
33
- with the `app_login_no_session_id` feature flag, which is off everywhere
34
- until deployed apps have moved to `appAuth`.
35
- - With that flag on, a failed handoff mint answers 422 with `data.errorCode` =
36
- `app_auth_exchange_unavailable` instead of degrading to the legacy response.
37
-
38
-
39
- ## Consumer Impact
40
-
41
- ### Login handoff for the fusebase-auth ops
42
-
43
- Purely additive: `session` is returned exactly as before, so no deployed app
44
- changes behaviour. Spend `appAuth.authPath` on the app's own origin, either
45
- with a same-origin `fetch` sending `Accept: application/json` or with a plain
46
- navigation. `exchangeToken` stays valid for its whole `expiresInSeconds`
47
- window, is not invalidated by being spent, and is not bound to the app it was
48
- minted for, so treat it as a credential.
49
- A challenge result is unchanged. If the mint fails the response is the legacy
50
- one, without `appAuth` — until the `app_login_no_session_id` flag is turned on,
51
- where the same failure is a 422 that should be answered with a fresh sign-in
52
- rather than a replay of the call.
53
-
54
-
55
- ## Verification
56
-
57
- ### Login handoff for the fusebase-auth ops
58
-
59
- - Unit tests over all four operations, the handoff URL encoding, the mint
60
- failure paths and the relative-path guard.
61
- - Generated contract/OpenAPI validation and service build.
@@ -1,9 +0,0 @@
1
- # Release Notes 2.11.6
2
-
3
- - Current ref: `HEAD`
4
- - Previous tag: `v2.11.6`
5
- - Generated at: 2026-09-09T09:28:57.596Z
6
-
7
- ## Included Drafts
8
-
9
- - None