selkies-project / selkies-project/selkies

Integrate Selkies to Jupyter, Coder, and Open OnDemand (just like noVNC)

Open
#64 113 comments 0 reactions 0 assignees View on GitHub
enhancement good first issue help wanted web
Dominant language
Python
Stars
2.2k
Forks
193
Avg merge
19h 47m
Merged PRs (30d)
38

Description

Can be AI-driven: Yes, as long as all cases are tested carefully in real-life by people as well as automated tests.
Expected to be workable with: Fable 5, GPT-5.6 Sol, Kimi-K3, Opus 5, or equivalent.
Prompt: Reference the below as well as respective projects.

---

Update:

We currently have this Coder configuration working self-hosted:

`entrypoint.sh` for Coder:

```bash
set -e
supervisord
```

Open main.tf

```terraform
terraform {
required_providers {
coder = {
source = "coder/coder"
version = "~> 0.6.17"
}
kubernetes = {
source = "hashicorp/kubernetes"
version = "~> 2.18"
}
}
}

provider "coder" {
feature_use_managed_variables = true
}

provider "kubernetes" {
config_path = var.use_kubeconfig == true ? "~/.kube/config" : null
}

data "coder_workspace" "me" {}

data "coder_parameter" "cpu" {
name = "CPU (cores)"
default = "2"
icon = "/icon/memory.svg"
mutable = true
option {
name = "2 Cores"
value = "2"
}
option {
name = "4 Cores"
value = "4"
}
option {
name = "6 Cores"
value = "6"
}
option {
name = "8 Cores"
value = "8"
}
option {
name = "16 Cores"
value = "16"
}
}

data "coder_parameter" "memory" {
name = "Memory (GB)"
default = "2"
icon = "/icon/memory.svg"
mutable = true
option {
name = "2 GB"
value = "2"
}
option {
name = "4 GB"
value = "4"
}
option {
name = "6 GB"
value = "6"
}
option {
name = "8 GB"
value = "8"
}
option {
name = "16 GB"
value = "16"
}
option {
name = "32 GB"
value = "32"
}
option {
name = "64 GB"
value = "64"
}
}

data "coder_parameter" "password" {
name = "password"
display_name = "Selkies Password"
description = "The Selkies password for authentication. User is ubuntu."
icon = "/emojis/1f511.png"
mutable = false
option {
name = "mypasswd"
value = "mypasswd"
}
}

data "coder_parameter" "home_disk_size" {
name = "home_disk_size"
display_name = "Home disk size"
description = "The size of the home disk in GB"
default = "100"
type = "number"
icon = "/emojis/1f4be.png"
mutable = false
validation {
min = 1
max = 99999
}
}

variable "use_kubeconfig" {
type = bool
description = "Use host kubeconfig? (true/false)"
default = false
}

variable "namespace" {
type = string
description = "The Kubernetes namespace to create workspaces in (must exist prior to creating workspaces)"
default = "coder"
}

resource "kubernetes_pod" "main" {
count = data.coder_workspace.me.start_count
metadata {
name = "coder-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
namespace = var.namespace
labels = {
"app.kubernetes.io/name" = "coder-workspace"
"app.kubernetes.io/instance" = "coder-workspace-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
"app.kubernetes.io/part-of" = "coder"
// Coder specific labels.
"com.coder.resource" = "true"
"com.coder.workspace.id" = data.coder_workspace.me.id
"com.coder.workspace.name" = data.coder_workspace.me.name
"com.coder.user.id" = data.coder_workspace.me.owner_id
"com.coder.user.username" = data.coder_workspace.me.owner
}
annotations = {
"com.coder.user.email" = data.coder_workspace.me.owner_email
}
}

spec {

container {
name = "dev"
image = "ghcr.io/selkies-project/nvidia-glx-desktop:latest"
image_pull_policy = "Always"
command = ["sh", "-c", coder_agent.main.init_script]
# security_context {
# run_as_user = "1000"
# privileged = true
# }

env {
name = "TZ"
value = "UTC"
}
env {
name = "DISPLAY_SIZEW"
value = "1920"
}
env {
name = "DISPLAY_SIZEH"
value = "1080"
}
env {
name = "DISPLAY_REFRESH"
value = "60"
}
env {
name = "DISPLAY_DPI"
value = "96"
}
env {
name = "DISPLAY_CDEPTH"
value = "24"
}
env {
name = "VIDEO_PORT"
value = "DFP"
}
env {
name = "PASSWD"
value = "${data.coder_parameter.password.value}"
}
env {
name = "SELKIES_ENCODER"
value = "nvh264enc"
}
env {
name = "SELKIES_ENABLE_RESIZE"
value = "false"
}
env {
name = "SELKIES_VIDEO_BITRATE"
value = "8000"
}
env {
name = "SELKIES_FRAMERATE"
value = "60"
}
env {
name = "SELKIES_AUDIO_BITRATE"
value = "128000"
}
env {
name = "SELKIES_ENABLE_BASIC_AUTH"
value = "true"
}
env {
name = "SELKIES_BASIC_AUTH_PASSWORD"
value = "${data.coder_parameter.password.value}"
}
env {
name = "SELKIES_TURN_REST_URI"
value = "http://turn-rest.nrp-nautilus.io"
}
env {
name = "SELKIES_TURN_PROTOCOL"
value = "tcp"
}
env {
name = "SELKIES_TURN_TLS"
value = "false"
}
env {
name = "CODER_AGENT_TOKEN"
value = coder_agent.main.token
}

stdin = true
tty = true

port {
name = "http"
container_port = 8080
protocol = "TCP"
}

resources {
limits = {
"cpu" = "${data.coder_parameter.cpu.value}"
"memory" = "${data.coder_parameter.memory.value}Gi"
"nvidia.com/gpu" = 1
}
requests = {
"cpu" = "${data.coder_parameter.cpu.value}"
"memory" = "${data.coder_parameter.memory.value}Gi"
"nvidia.com/gpu" = 1
}
}

volume_mount {
mount_path = "/home/ubuntu/persistent"
name = "home"
read_only = false
sub_path = "home"
}

volume_mount {
mount_path = "/dev/shm"
name = "dshm"
}
}

dns_policy = "None"
dns_config {
nameservers = ["8.8.8.8", "8.8.4.4"]
}

volume {
name = "dshm"
empty_dir {}
}

volume {
name = "home"
persistent_volume_claim {
claim_name = kubernetes_persistent_volume_claim.home.metadata.0.name
read_only = false
}
}

affinity {
node_affinity {
required_during_scheduling_ignored_during_execution {
node_selector_term {
match_expressions {
key = "topology.kubernetes.io/zone"
operator = "NotIn"
values = ["myzone"]
}
}
}
}
}
}
}

resource "coder_agent" "main" {
os = "linux"
arch = "amd64"
login_before_ready = false
startup_script_timeout = 180
startup_script = <<-EOT
set -e

# install and start code-server
curl -fsSL https://code-server.dev/install.sh | sh -s -- --method=standalone --prefix=/tmp/code-server --version 4.8.3
/tmp/code-server/bin/code-server --auth none --port 13337 >/tmp/code-server.log 2>&1 &

echo "Initializing Supervisor..."
nohup supervisord
EOT
}

resource "coder_app" "code-server" {
agent_id = coder_agent.main.id
slug = "code-server"
display_name = "code-server"
icon = "/icon/code.svg"
url = "http://localhost:13337?folder=/home/coder"
subdomain = false
share = "owner"

healthcheck {
url = "http://localhost:13337/healthz"
interval = 3
threshold = 10
}
}

resource "kubernetes_persistent_volume_claim" "home" {
metadata {
name = "coder-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}-home"
namespace = var.namespace
labels = {
"app.kubernetes.io/name" = "coder-pvc"
"app.kubernetes.io/instance" = "coder-pvc-${lower(data.coder_workspace.me.owner)}-${lower(data.coder_workspace.me.name)}"
"app.kubernetes.io/part-of" = "coder"
// Coder specific labels.
"com.coder.resource" = "true"
"com.coder.workspace.id" = data.coder_workspace.me.id
"com.coder.workspace.name" = data.coder_workspace.me.name
"com.coder.user.id" = data.coder_workspace.me.owner_id
"com.coder.user.username" = data.coder_workspace.me.owner
}
annotations = {
"com.coder.user.email" = data.coder_workspace.me.owner_email
}
}
wait_until_bound = false
spec {
access_modes = ["ReadWriteOnce"]
storage_class_name = "rook-ceph-block"
resources {
requests = {
storage = "${data.coder_parameter.home_disk_size.value}Gi"
}
}
}
}

resource "coder_app" "selkies" {
agent_id = coder_agent.main.id
slug = "selkies"
display_name = "Selkies"
icon = "/emojis/1f3ae.png"
url = "http://localhost:8080"
subdomain = true
share = "owner"
}
```

---

Self-explanatory. Just like [VS Code Server](https://github.com/ryanlovett/nbnovnc) and [noVNC](https://github.com/jupyterhub/jupyter-remote-desktop-proxy), a button click in Jupyter should lead to a window with Selkies.
This will help greatly in robotics, simulations, and other kinds of research.

External contribution dearly expected.

It can be a separate project, a PR, or any other form of contribution.
Integration with [Jupyter Docker containers](https://jupyter-docker-stacks.readthedocs.io/en/latest/) should also be possible.

Creating a template for [Coder](https://github.com/coder/coder) would also be of interest.

Contributor guide

No contributing guide indexed for this repository

Research direction

Review the existing noVNC and VS Code Server integration references, along with the provided Coder Terraform configuration and Jupyter Docker Stacks documentation. Define separate integration paths for Jupyter, Coder, and Open OnDemand, then test each in real deployments and with automated checks. Done means a Jupyter button opens Selkies and equivalent documented integrations or templates work for the other environments.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, jupyter, kubernetes, terraform
Domain
cloud, desktop, devops
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.