selkies-project / selkies-project/selkies
Integrate Selkies to Jupyter, Coder, and Open OnDemand (just like noVNC)
- 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