diff --git a/components/dashboard/src/data/git-providers/unified-repositories-search-query.ts b/components/dashboard/src/data/git-providers/unified-repositories-search-query.ts
index 1b8da312660c5a..4c9d250c4c35d1 100644
--- a/components/dashboard/src/data/git-providers/unified-repositories-search-query.ts
+++ b/components/dashboard/src/data/git-providers/unified-repositories-search-query.ts
@@ -128,15 +128,12 @@ export function deduplicateAndFilterRepositories(
     if (results.length === 0) {
         // If the searchString is a URL, and it's not present in the proposed results, "artificially" add it here.
         if (isValidGitUrl(searchString)) {
-            console.log("It's valid man");
             results.push(
                 new SuggestedRepository({
                     url: searchString,
                 }),
             );
         }
-
-        console.log("Valid after man");
     }
 
     // Limit what we show to 200 results
@@ -145,7 +142,7 @@ export function deduplicateAndFilterRepositories(
 
 const ALLOWED_GIT_PROTOCOLS = ["ssh:", "git:", "http:", "https:"];
 /**
- * An opionated git URL validator
+ * An opinionated git URL validator
  *
  * Assumptions:
  * - Git hosts are not themselves TLDs (like .com) or reserved names like `localhost`
diff --git a/components/dashboard/src/prebuilds/detail/PrebuildDetailPage.tsx b/components/dashboard/src/prebuilds/detail/PrebuildDetailPage.tsx
index cbc4349abaebac..0cebed55be2c5a 100644
--- a/components/dashboard/src/prebuilds/detail/PrebuildDetailPage.tsx
+++ b/components/dashboard/src/prebuilds/detail/PrebuildDetailPage.tsx
@@ -361,15 +361,13 @@ export const PrebuildDetailPage: FC = () => {
                                     >
                                         View Prebuild Settings
                                     
-                                    
+                                    
                                 
                             
                         
diff --git a/components/server/src/prebuilds/prebuild-manager.ts b/components/server/src/prebuilds/prebuild-manager.ts
index 072496bbe81e17..aaf9cc63ae9e84 100644
--- a/components/server/src/prebuilds/prebuild-manager.ts
+++ b/components/server/src/prebuilds/prebuild-manager.ts
@@ -49,6 +49,7 @@ export interface StartPrebuildParams {
     commitInfo?: CommitInfo;
     forcePrebuild?: boolean;
     trigger?: keyof ProjectUsage;
+    assumeProjectActive?: boolean;
 }
 
 export interface PrebuildFilter {
@@ -330,7 +331,15 @@ export class PrebuildManager {
 
     async startPrebuild(
         ctx: TraceContext,
-        { context, project, user, commitInfo, forcePrebuild, trigger = "lastWebhookReceived" }: StartPrebuildParams,
+        {
+            context,
+            project,
+            user,
+            commitInfo,
+            forcePrebuild,
+            trigger = "lastWebhookReceived",
+            assumeProjectActive,
+        }: StartPrebuildParams,
     ): Promise {
         const span = TraceContext.startSpan("startPrebuild", ctx);
         const cloneURL = context.repository.cloneUrl;
@@ -461,7 +470,10 @@ export class PrebuildManager {
                     "Prebuild is rate limited. Please contact Gitpod if you believe this happened in error.";
                 await this.workspaceDB.trace({ span }).storePrebuiltWorkspace(prebuild);
                 span.setTag("ratelimited", true);
-            } else if (await this.projectService.isProjectConsideredInactive(user.id, project.id)) {
+            } else if (
+                !assumeProjectActive &&
+                (await this.projectService.isProjectConsideredInactive(user.id, project.id))
+            ) {
                 prebuild.state = "aborted";
                 prebuild.error =
                     "Project is inactive. Please start a new workspace for this project to re-enable prebuilds.";
@@ -661,7 +673,7 @@ export class PrebuildManager {
         if (!prebuild || !organizationId) {
             throw new ApplicationError(ErrorCodes.PRECONDITION_FAILED, "prebuild workspace not found");
         }
-        await this.auth.checkPermissionOnOrganization(userId, "read_prebuild", organizationId);
+        await this.auth.checkPermissionOnProject(userId, "read_prebuild", organizationId);
 
         const instance = await this.workspaceService.getCurrentInstance(userId, prebuild.workspace.id, {
             skipPermissionCheck: true,
diff --git a/components/server/src/projects/projects-service.ts b/components/server/src/projects/projects-service.ts
index 9a5bb727965b4a..19e820cfc4b085 100644
--- a/components/server/src/projects/projects-service.ts
+++ b/components/server/src/projects/projects-service.ts
@@ -62,8 +62,14 @@ export class ProjectsService {
         @inject(InstallationService) private readonly installationService: InstallationService,
     ) {}
 
-    async getProject(userId: string, projectId: string): Promise {
-        await this.auth.checkPermissionOnProject(userId, "read_info", projectId);
+    /**
+     * Returns a project by its ID.
+     * @param skipPermissionCheck useful either when the caller already checked permissions or when we need to do something purely server-side (e.g. looking up a project when starting a workspace by a collaborator)
+     */
+    async getProject(userId: string, projectId: string, skipPermissionCheck?: boolean): Promise {
+        if (!skipPermissionCheck) {
+            await this.auth.checkPermissionOnProject(userId, "read_info", projectId);
+        }
         const project = await this.projectDB.findProjectById(projectId);
         if (!project) {
             throw new ApplicationError(ErrorCodes.NOT_FOUND, `Project ${projectId} not found.`);
@@ -132,11 +138,18 @@ export class ProjectsService {
         return filteredProjects;
     }
 
-    async findProjectsByCloneUrl(userId: string, cloneUrl: string, organizationId?: string): Promise {
+    async findProjectsByCloneUrl(
+        userId: string,
+        cloneUrl: string,
+        organizationId?: string,
+        skipPermissionCheck?: boolean,
+    ): Promise {
         const projects = await this.projectDB.findProjectsByCloneUrl(cloneUrl, organizationId);
         const result: Project[] = [];
         for (const project of projects) {
-            if (await this.auth.hasPermissionOnProject(userId, "read_info", project.id)) {
+            const hasPermission =
+                skipPermissionCheck || (await this.auth.hasPermissionOnProject(userId, "read_info", project.id));
+            if (hasPermission) {
                 result.push(project);
             }
         }
diff --git a/components/server/src/workspace/context-service.ts b/components/server/src/workspace/context-service.ts
index 1f09b817d8f7d7..3ce0f82011d33c 100644
--- a/components/server/src/workspace/context-service.ts
+++ b/components/server/src/workspace/context-service.ts
@@ -158,7 +158,9 @@ export class ContextService {
                 user.id,
                 context.repository.cloneUrl,
                 options?.organizationId,
+                true,
             );
+            // todo(ft): solve for this case with collaborators who can't select projects directly
             if (projects.length > 1) {
                 throw new ApplicationError(ErrorCodes.BAD_REQUEST, "Multiple projects found for clone URL.");
             }
diff --git a/components/server/src/workspace/suggested-repos-sorter.ts b/components/server/src/workspace/suggested-repos-sorter.ts
index 04ddf73bbda2c8..37713abf349908 100644
--- a/components/server/src/workspace/suggested-repos-sorter.ts
+++ b/components/server/src/workspace/suggested-repos-sorter.ts
@@ -20,7 +20,7 @@ export const sortSuggestedRepositories = (repos: SuggestedRepositoryWithSorting[
     // This allows us to consider the lastUse of a recently used project when sorting
     // as it will may have an entry for the project (no lastUse), and another for recent workspaces (w/ lastUse)
 
-    const projectURLs: string[] = [];
+    let projectURLs: string[] = [];
     let uniqueRepositories: SuggestedRepositoryWithSorting[] = [];
 
     for (const repo of repos) {
@@ -88,7 +88,7 @@ export const sortSuggestedRepositories = (repos: SuggestedRepositoryWithSorting[
     uniqueRepositories = uniqueRepositories.map((repo) => {
         if (repo.projectId && !repo.projectName) {
             delete repo.projectId;
-            delete projectURLs[projectURLs.indexOf(repo.url)];
+            projectURLs = projectURLs.filter((url) => url !== repo.url);
         }
 
         return repo;
diff --git a/components/server/src/workspace/workspace-service.ts b/components/server/src/workspace/workspace-service.ts
index defd384e1f3c33..f968164754859b 100644
--- a/components/server/src/workspace/workspace-service.ts
+++ b/components/server/src/workspace/workspace-service.ts
@@ -455,6 +455,7 @@ export class WorkspaceService {
                 forcePrebuild: false,
                 context,
                 trigger: "lastWorkspaceStart",
+                assumeProjectActive: true,
             });
             log.info(logCtx, "starting prebuild after workspace creation", {
                 projectId: project.id,
@@ -819,7 +820,7 @@ export class WorkspaceService {
         }
 
         const projectPromise = workspace.projectId
-            ? ApplicationError.notFoundToUndefined(this.projectsService.getProject(user.id, workspace.projectId))
+            ? ApplicationError.notFoundToUndefined(this.projectsService.getProject(user.id, workspace.projectId, true))
             : Promise.resolve(undefined);
 
         await mayStartPromise;
@@ -866,7 +867,7 @@ export class WorkspaceService {
             result = await this.entitlementService.mayStartWorkspace(user, organizationId, runningInstances);
             TraceContext.addNestedTags(ctx, { mayStartWorkspace: { result } });
         } catch (err) {
-            log.error({ userId: user.id }, "EntitlementSerivce.mayStartWorkspace error", err);
+            log.error({ userId: user.id }, "EntitlementService.mayStartWorkspace error", err);
             TraceContext.setError(ctx, err);
             return; // we don't want to block workspace starts because of internal errors
         }
diff --git a/components/server/src/workspace/workspace-starter.ts b/components/server/src/workspace/workspace-starter.ts
index 605d5e358c1750..faff4301894f49 100644
--- a/components/server/src/workspace/workspace-starter.ts
+++ b/components/server/src/workspace/workspace-starter.ts
@@ -566,8 +566,8 @@ export class WorkspaceStarter {
             return;
         }
 
-        // implicit project (existing on the same clone URL)
-        const projects = await this.projectService.findProjectsByCloneUrl(user.id, contextURL, organizationId);
+        // implicit project (existing on the same clone URL). We skip the permission check so that collaborators are not stuck
+        const projects = await this.projectService.findProjectsByCloneUrl(user.id, contextURL, organizationId, true);
         if (projects.length === 0) {
             throw new ApplicationError(
                 ErrorCodes.PRECONDITION_FAILED,
@@ -1951,10 +1951,12 @@ export class WorkspaceStarter {
                 {},
             );
             if (isEnabledPrebuildFullClone) {
-                const project = await this.projectService.getProject(user.id, workspace.projectId).catch((err) => {
-                    log.error("failed to get project", err);
-                    return undefined;
-                });
+                const project = await this.projectService
+                    .getProject(user.id, workspace.projectId, true)
+                    .catch((err) => {
+                        log.error("failed to get project", err);
+                        return undefined;
+                    });
                 if (project && project.settings?.prebuilds?.cloneSettings?.fullClone) {
                     result.setFullClone(true);
                 }
diff --git a/components/spicedb/schema/schema.yaml b/components/spicedb/schema/schema.yaml
index c021f451add539..858bda86f3adbe 100644
--- a/components/spicedb/schema/schema.yaml
+++ b/components/spicedb/schema/schema.yaml
@@ -92,6 +92,8 @@ schema: |-
       permission read_billing = member + owner + installation->admin
       permission write_billing = owner + installation->admin
 
+      // Note that there are two different read_prebuild permissions: this one, guarding primarily the listPrebuilds method in the API and then the other under projects, which is the actual permission used for checking if a user can use a prebuild for a repository.
+      // Today, the difference is that collaborators can't read prebuilds on an org but can on a repository (project).
       permission read_prebuild = member + owner + installation->admin
 
       permission create_workspace = member + collaborator
@@ -118,10 +120,10 @@ schema: |-
       permission write_info = editor + org->owner + org->installation_admin
       permission delete = editor + org->owner + org->installation_admin
 
-      permission read_env_var = viewer + editor + org->owner + org->installation_admin
+      permission read_env_var = viewer + editor + org->collaborator + org->owner + org->installation_admin
       permission write_env_var = editor + org->owner + org->installation_admin
 
-      permission read_prebuild = viewer + editor + org->owner + org->installation_admin
+      permission read_prebuild = viewer + editor + org->collaborator + org->owner + org->installation_admin
       permission write_prebuild = editor + org->owner
     }