* [CLC-2032] Block login and workspace operations for Classic PAYG sunset
Implement feature flag-based blocking for Gitpod Classic PAYG users:
Backend:
- Add utility functions to check if user is blocked by sunset
- Block login attempts in /login route handler, redirect to app.ona.com
- Block workspace creation and start operations in workspace-service-api
- Exempt users with roles/permissions and users in exempted organizations
Frontend:
- Update login page to show 'Login with Ona' button when sunset is enabled
- Keep SSO login form visible for exempted organizations
- Hide sunset notice banner when flag is enabled
- Update heading to 'Gitpod Classic has sunset'
Feature flag: classic_payg_sunset_enabled (JSON with enabled boolean and exemptedOrganizations array)
Co-authored-by: Ona <no-reply@ona.com>
* Add oldLogin parameter to show full login UI for exempted orgs
When sunset is enabled on gitpod.io, users now see a simplified UI:
- 'Continue with Ona' button (default)
- Link to show all login options (?oldLogin=true)
With ?oldLogin=true parameter:
- Shows all OAuth provider buttons
- Shows SSO login form
- Full functionality for exempted organizations
The link preserves returnToPath parameter if present.
Co-authored-by: Ona <no-reply@ona.com>
* Refactor backend sunset checks into separate functions
Split sunset blocking logic into two functions:
- isUserLoginBlockedBySunset: checks roles/permissions exemption for login
- isWorkspaceStartBlockedBySunset: checks org-level exemption for workspace ops
Move ClassicPaygSunsetConfig interface to gitpod-protocol for reusability.
Pass organizationId explicitly to workspace blocking checks.
Co-authored-by: Ona <no-reply@ona.com>
* Use typed ClassicPaygSunsetConfig in frontend feature flag
Import ClassicPaygSunsetConfig type from gitpod-protocol and use it
as the default value for classic_payg_sunset_enabled feature flag.
This leverages TypeScript's generic type inference in useFeatureFlag:
- useFeatureFlag<K extends keyof FeatureFlags> returns FeatureFlags[K]
- For classic_payg_sunset_enabled, it now returns ClassicPaygSunsetConfig
- Other flags continue to return their respective types (boolean, string, etc.)
Updated Login.tsx to access .enabled property with type guard to handle
the union type (ClassicPaygSunsetConfig | boolean) during loading state.
This ensures type safety and consistency between frontend and backend.
Co-authored-by: Ona <no-reply@ona.com>
* Parse JSON string for classic_payg_sunset_enabled feature flag
ConfigCat text flags return strings, so we need to parse JSON on both
frontend and backend.
Backend (featureflags.ts):
- Send JSON.stringify(defaultConfig) to ConfigCat
- Parse returned string with JSON.parse()
- Handle errors gracefully with fallback to default
Frontend (featureflag-query.ts):
- Add parseFeatureFlagValue() helper for JSON flags
- Send stringified default for classic_payg_sunset_enabled
- Parse returned string value
- Maintain type safety with generic return types
This allows ConfigCat to store the flag as text while maintaining
the typed object structure in our code.
Co-authored-by: Ona <no-reply@ona.com>
* Exempt dedicated installations from sunset blocking
Add isDedicatedInstallation parameter to sunset check functions.
Dedicated installations always return false (not blocked) regardless
of feature flag state.
Changes:
- isUserLoginBlockedBySunset: add isDedicatedInstallation param
- isWorkspaceStartBlockedBySunset: add isDedicatedInstallation param
- UserController: pass config.isDedicatedInstallation to login check
- WorkspaceServiceAPI: inject Config and pass isDedicatedInstallation
This ensures the sunset only affects gitpod.io (PAYG) and not
dedicated installations.
Co-authored-by: Ona <no-reply@ona.com>
* update login page for Gitpod classic `gitpod.io` users
* fix
* Improve sunset UI: use primary button and remove redundant subheading
Co-authored-by: Ona <no-reply@ona.com>
* Update sunset UI heading to focus on Ona value proposition
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
Co-authored-by: Siddhant Khare <siddhant@gitpod.io>
Co-authored-by: Cornelius A. Ludmann <github@cornelius-ludmann.de>
* fix: resolve workspace timeout parsing bug for mixed-unit durations
Fixes critical bug where organization timeout settings like '90m' (displayed as '1h30m')
were incorrectly parsed as '1m' instead of the intended 90 minutes.
Root cause: Custom parsing logic used:
- duration.slice(-1) to get unit (only last character)
- parseInt(duration.slice(0, -1), 10) to get value (stopped at first non-digit)
This caused '1h30m' → '1m', '2h15m' → '2m', etc.
Solution: Replace custom validation with @arcjet/duration library:
- Exact TypeScript port of Go's time.ParseDuration
- Handles all Go duration formats correctly including mixed units
- Zero dependencies, professionally maintained
- Comprehensive test coverage added
Impact: Organization admins can now set workspace timeouts like '90m'
and they will correctly result in 90-minute timeouts instead of 1-minute.
Co-authored-by: Ona <no-reply@ona.com>
* fix: migrate from @arcjet/duration to parse-duration library
- Replace @arcjet/duration with parse-duration for better Go duration format support
- Fix workspace timeout validation to handle milliseconds instead of seconds
- Add regex validation to reject bare numbers without units
- Update parseGoDurationToMs to handle null returns properly
- All 108 tests passing, mixed-unit duration bug completely resolved
The @arcjet/duration library had usage warnings and parsing issues with
mixed-unit durations like '1h30m' being incorrectly parsed as '1m'.
parse-duration is better maintained (367 dependents, 285k weekly downloads),
has zero dependencies, and provides perfect Go duration format compatibility.
Co-authored-by: Ona <no-reply@ona.com>
* fix: handle empty/whitespace strings in parseGoDurationToMs
The parseGoDurationToMs function was throwing errors for empty strings
and whitespace-only strings, but these should return 0 duration.
This was causing failures in public-api tests that expect empty strings
to be converted to 0 duration.
- Handle empty or whitespace-only strings as 0 duration
- Maintain existing error handling for invalid duration formats
- All tests now pass (108/108 gitpod-protocol, 87/87 public-api)
Co-authored-by: Ona <no-reply@ona.com>
---------
Co-authored-by: Ona <no-reply@ona.com>
* [dashboard] Initial infra rollout page, incl. list running workspaces
* [server, db, dashboard] Allow org-owner to stop workspace on all workspaces in the organization
Also, fix maintenanceMode update
* [public-api, db, server, dashboard] Introduce MaintenanceNofitication banner that can be configured per org
* review comments: use mutation instead of callback for state mutation
* Fix workspace start prevention
* Review comments around banners and rendering
- permissions issues
- add banner to /new page
- a bunch of rendering issues and alignments
- renaming some things for clarity (dropped "Scheduled" prefixes)
- only allow up to 255 characters in notification messages
* [dashboard] Only show Admin entry for dedicated
* [server] Fix permissions for setMaintenanceMode to "maintenance"
* [dashboard] Adjusted copy incl. default notification message
* Review coments: re-use and fix styles, and naming
Co-authored-by: Filip Troníček <filip@gitpod.io>
* Minor copy improvements
* [server] Fix bogus permission check in stopWorkspace
---------
Co-authored-by: Filip Troníček <filip@gitpod.io>
* [dev] Bump grpc/grpc-js 1.10.8 -> 1.12.6 and authzed/authzed-node 0.15.0 -> 1.2.2
Tool: gitpod/catfood.gitpod.cloud
* [server] Streamline spicedb gRPC client usage and creation options
- instead of doing retries on two levels, rely on the gRPC-level retries
- to mitigate the loss of insights, introduce createDebugLogInterceptor
- client options: use sane defaults derived from the documentation instead of the excessive ones we had in place before
- use "waitForReady" option: it should a) make our calls for responsive on re-connects, while b) - because we keep re-trying on DEADLINE_EXCEEDED - should be as reliable as before
Tool: gitpod/catfood.gitpod.cloud
* [protocol] Centralize grpc.isConnectionAlive
Tool: gitpod/catfood.gitpod.cloud
* [server] SpiceDB client: retry with new client on "Waiting for LB pick" error
Tool: gitpod/catfood.gitpod.cloud
* [docker-up] Minor fixes and add tests
Tool: gitpod/catfood.gitpod.cloud
* [image-builder-bob] Tests&fixes from an experimental PR
Tool: gitpod/catfood.gitpod.cloud
* [supervisor, ws-manager] Write docker credentials into client config file if passed into workspace
Tool: gitpod/catfood.gitpod.cloud
* [server] Introduce project.settings.enableDockerdAuthentication and expose it on the API
Tool: gitpod/catfood.gitpod.cloud
* [dashboard] Add "Docker registry authentication" toggle under projects/env vars
Tool: gitpod/catfood.gitpod.cloud
* [server] Guard project.settings.enableDockerdAuthentication by org write_settings permission
Tool: gitpod/catfood.gitpod.cloud
* review comments with cleanup + small fixes
Tool: gitpod/catfood.gitpod.cloud
* [supervisor] Add Gitpod commit annotation
* server and API changes
* [dashboard] add org setting for commit annotation
* Fix things
* Fix label for annotation switch
* Revert accidental rename
* minor docs fixes
* Add a feature flag for the setting: `commit_annotation_setting_enabled`
* Register hook in the cloned repo instead of under /etc/
* don't override existing hooks
* `gp git-commit-message-helper` to use `git interpret-trailers`
* Test it!
* 🧹 indeed
* Update timestamp of DB migration
* Introduce `max_parallel_running_workspaces` for orgs
* Add migration
* Add UI and org settings server changes
* Rough edges
* Number inputs (:trollface:)
* Move LazyOrganizationService to UBP entitlement service to get rid of circ. dep. errs
* Fix tests
* Update components/gitpod-db/src/typeorm/entity/db-team-settings.ts
Co-authored-by: Gero Posmyk-Leinemann <gero@gitpod.io>
* Rename `isPaidPlan` to `isPaidOrDedicated` and get rid of `update_max_parallel_running_workspaces`
* Update components/server/src/workspace/workspace-service.ts
Co-authored-by: Gero Posmyk-Leinemann <gero@gitpod.io>
* Deduplicate instance counting logic
---------
Co-authored-by: Gero Posmyk-Leinemann <gero@gitpod.io>
* [server config] Introduce isDedicatedInstallation, and use it to replace isSIngleOrgInstallation
incl. further cleanup around getConfiguration and server config
* [server, dashboard] Remove enableDedicatedOnboardingFlow feature flag and replace is with getInstallationConfiguration.IsDedicatedInstallation
* [dashboard, server] Remove "sinlgeOrgMode"
* [server] OrganizationService: block createTeam consistently for org-owned users
* [server, dashboard] Introduce "enable_multi_org" feature flag to allow admin-user to create organizations
* [dashboard] introduce "/?orgSlug=", which allows to pre-select an org in a "create workspace" URL (e.g. "/?orgSlug=org1#github.com/my/repo")
* [db] Auto-delete container "test-mysql" if it's already present
* fix tests
* [dashboard] Check if localStorage is available before using it
* [dashboard] SSOLogin: fix orgSlug source precedence to: path/search/localStorage
* [server] Deny "joinOrganization" for org-owned users
* Gpl/970-multi-org-tests (#20436)
* fix tests for real
* [server] Create OrgService.createOrgOwnedUser, and use that across tests to fix the "can't join org" permission issues
* Update components/server/src/orgs/organization-service.ts
Co-authored-by: Filip Troníček <filip@gitpod.io>
---------
Co-authored-by: Filip Troníček <filip@gitpod.io>
---------
Co-authored-by: Filip Troníček <filip@gitpod.io>
* Add WS image metrics to workspace instances
* Update tests
* fix ws-manager-api field description
* Prefer existing DB values for metrics
* Copy proto comments over to protocol type
* [context parser] Check current ref for Docker image existence
* Better builder bob errors from supervisor
* Gently soft-fail when the Dockerfile isn't found
* Add warning to ws metadata when starting workspace
* Introduce a magic constant instead of empty strings
* Improve supervisor failed reading bob log error
* Fixup cloning our special SHA
* idk what happened
* let workspaces start even on invalid docker refs
* add proto
* codegen
* impl
* WIP UI
* make it work
* Make it work
* Empty state
* Update copies (thx Fernando!)
* Fix tip flexbox
* fix newline for role restriction empty state
* When arbitrary repos are restricted, don't suggest them
* proto update
Co-authored-by: Filip Troníček <filip@gitpod.io>
Co-authored-by: Gero Posmyk-Leinemann <32448529+geropl@users.noreply.github.com>
* dashboard + server
---------
Co-authored-by: Filip Troníček <filip@gitpod.io>
Co-authored-by: Gero Posmyk-Leinemann <32448529+geropl@users.noreply.github.com>
* [PAPI] Retry steams upon not receiving data for 10 seconds
* [PAPI] Retry steams upon not receiving data for 10 seconds (#20176)
* debug message
* Do not listen for ws updates globally and do not timeout for those calls
* Timeout signal as a getter
* use ff for disablement
* accesor fn => getter
* send empty initial data in papi for global ws watch requests
---------
Co-authored-by: Gero Posmyk-Leinemann <gero@gitpod.io>
* Organization timeout defaults proto
* Generated code
* Implement TimeoutSettings on the API layer
* Add timeout setting UI
* Actually change default workspace timeout based on org settings
workspace starter fixes
* Add alert for free plans
* denyUserTimeouts
* [dashboard] Fix check for "timeout config eligibility" by using BillingMode
* [server] Drop FF "disable_set_timeout"
* fix
* Move to team policies
* don't include `timeoutSettings` if not set
* [dashboard] TeamPolicies: Disable "save" if workspace timeouts are disabled
---------
Co-authored-by: Gero Posmyk-Leinemann <gero@gitpod.io>
* [json-rpc] Fix encoding of watchImageBuildLogs data to number[]
Because json-rpc can't handle complex objects like UInt8Array properly.
Conversion is done using "Array.from(UInt8Array)" and "new UInt8Array(data)"
* [dashboard] Fix "workspaceId is required" errors
* Review suggestions
Co-authored-by: Filip Troníček <filip@gitpod.io>
---------
Co-authored-by: Filip Troníček <filip@gitpod.io>
* [supervisor] Make sure we read the last chunk of message when task is closed
* [server, dashboard] Prebuild logs: stream as UInt8Array instead of string, and make sure we send the last chunk before closing the response
* Make log buffer more efficient
Merging done with `TypedArray.prototype.set()` as suggested here:
https://stackoverflow.com/a/73074813/10199319
* [dashboard] PrebuildTaskTab: use hash(err.message) as toastId
* [server] prebuild logs: Only call res.end after a timeout, so clients can continue reading from the stream
* [dashboard] prebuild logs emitter: If we receive code "200", forward prefixChunk, and stop streaming
* fixup! [dashboard] PrebuildTaskTab: use hash(err.message) as toastId
---------
Co-authored-by: Filip Troníček <filip@gitpod.io>
* Start prebuilds on started workspaces
* Fix circl dep
* move out to workspace creation
* Do it in the workspace service
* remove webhook tests
* remove webhook handling code
* Revert "remove webhook handling code"
This reverts commit d7c4550ec6b16091344607bfeee234b1f0e5705b.
* Anotate new setups with an `activity-based` activation strategy
* Update tests
* Alert banner
* add correct changelog path
* fix tests
* Remove code for registering prebuilds
* Still pass prebuild preconditions in order to run
* Re-add link to prebuild history of a repo
* Rename to trigger strategy and change project activity indicator
* redirect in the offline case
* [public-api] generate noise
* [server] Refactor Prebuild resolution to happen in a single place
* [dashboard, api] Show Prebuild details
* fixup! [server] Refactor Prebuild resolution to happen in a single place
* [bridge] Revive Prebuild QUEUED state: it's everything before running
* [server] Fix dead-end for streaming logs when starting too early
* [dashboard] Adds SHA + duration, fix re-rendering when prebuildId changes, and uses the new/old streaming logic
WIP because still has the "duplicate (sometimes triple!) logs" react re-rendering issue
* Prevent unnecessary re-renders of task output
* remove double-comment
* Duration display improvements
* Properly dismiss toasts and render durations
* Remove SHA from prebuild list
* Clean up and implement the `reset` event for workspace logs
* fix comment
* Tiny cleanup
---------
Co-authored-by: Filip Troníček <filip@gitpod.io>