Plane: self-hosted proje yönetimi + kendi modeliniz
Plane'i Kaptan'a kurun, AI'ını kurum içindeki Qwen'e bağlayın. Relay'in arkasında CSRF, imajın kendi ortam değişkenlerini ezmesi ve model beyaz listesi — üçünün de çözümü.
Plane açık kaynak bir iş takip sistemidir — Jira'ya benzer bir pano: projeler, iş kalemleri, cycle'lar, modüller. Topluluk sürümü tamamen self-hosted çalışır ve içinde küçük bir AI vardır: iş kalemi açarken başlıktan açıklama üretir.
Bu sayfa Plane'i Gao Kaptan'a kuruyor ve o AI'ı kurum içindeki Qwen3.8'e bağlıyor. Sonuçta iş kalemi açarken üretilen metin kurumun kendi GPU'sunda üretiliyor; hiçbir veri OpenAI'a gitmiyor.
Tarayıcı ──▶ relay ──▶ plane-app ──▶ plane-llm (LiteLLM köprüsü)
│ │
plane-db / redis / mq / minio │
Kaptan inference · Qwen3.8-27B-FP8Kurduğumuz düzende yedi pod var: plane-db, plane-redis, plane-mq, plane-minio,
plane-app, AI için plane-llm ve işleri yöneten plane-agent.
Beklentiyi baştan netleştirelim. Plane'in tanıtımlarındaki "Pi" asistanı — workspace'i
bilen, sohbet eden, sizin yerinize iş kalemi açıp güncelleyen AI — Cloud ve Commercial
sürümlerde. plane-aio-community imajında o kod yoktur. Topluluk sürümünde AI'ın tamamı
iki uçtan ibarettir (GPTIntegrationEndpoint, WorkspaceGPTIntegrationEndpoint) ve ikisi de
tek iş yapar: verdiğiniz başlıktan metin üretmek. Bu sayfanın bağladığı şey odur.
Kurulum
Dört arka servis
All-in-one imaj web/api/worker/beat/live/space bileşenlerinin hepsini tek konteynerde çalıştırır, ama veritabanını ve kuyruğu kendisi getirmez. Önce onlar:
| Pod | İmaj | Port | Ortam değişkenleri |
|---|---|---|---|
plane-db | postgres:15.7-alpine | 5432 | POSTGRES_DB/USER=plane, POSTGRES_PASSWORD, PGDATA=/var/lib/postgresql/data/pgdata |
plane-redis | valkey/valkey:7.2.11-alpine | 6379 | — |
plane-mq | rabbitmq:3.13.6-management-alpine | 5672 | RABBITMQ_DEFAULT_USER/PASS, RABBITMQ_DEFAULT_VHOST=plane |
plane-minio | minio/minio | 9000 | MINIO_ROOT_USER/PASSWORD, komut: minio server /export --console-address :9090 |
plane-db ve plane-minio kalıcı disk ister (/var/lib/postgresql/data ve /export).
Dördü de küme içinde kalır — dışarı açılmaz. Uygulama onlara servis DNS'i ile ulaşır:
plane-db-svc.<namespace>.svc.cluster.local:5432.
Uygulama pod'u
makeplane/plane-aio-community:v1.4.2, web_port: 80, dışarı açık. Ortam değişkenleri:
| Anahtar | Değer | Neden |
|---|---|---|
DOMAIN_NAME | kptnpods.isnet.net.tr | Boot koşulu. APP_DOMAIN yetmez, açılış betiği bu adı arar |
APP_PROTOCOL | https | |
WEB_URL | relay adresi | Ön yüze verilen bağlantılar |
CORS_ALLOWED_ORIGINS | https://kptnpods.isnet.net.tr,https://*.kaptan.isnet.net.tr | Joker şart — aşağıda Tuzak 2 |
SECRET_KEY, LIVE_SERVER_SECRET_KEY | rastgele 64 hex | |
DATABASE_URL, REDIS_URL, AMQP_URL | servis DNS'leri | |
USE_MINIO | 1 | Path-style adresleme |
AWS_REGION | us-east-1 | MinIO umursamaz ama boş olamaz, boot kontrolü reddediyor |
AWS_ACCESS_KEY_ID / SECRET / AWS_S3_ENDPOINT_URL / AWS_S3_BUCKET_NAME | MinIO bilgileri | |
OPENAI_BASE_URL | http://plane-llm-svc.<ns>.svc.cluster.local:4000/v1 | AI'ı köprüye çevirir — aşağıda |
Ayrıca pod'a bir dosya ve bir komut veriyoruz; sebebi Tuzak 1:
# /plane-wrapper.sh — pod komutu: ["sh", "/plane-wrapper.sh"]
sed -i 's|^\(.*update_env_value "CORS_ALLOWED_ORIGINS" "[^"]*\)"|\1,https://*.kaptan.isnet.net.tr"|' /app/start.sh
exec bash /app/start.shİlk açılış
Pod ayağa kalkınca relay bağlantısını açın, hesabı oluşturun ve çalışma alanı + proje açın. Bu noktada Plane çalışır durumdadır — AI henüz yok.

AI köprüsü
Plane'in AI'ını kendi modelinize bağlamanın önünde üç engel var ve üçü de birbirinden bağımsız:
1. Model beyaz listesi. plane/app/views/external/base.py, verilen model adını
OpenAIProvider.models listesine karşı doğrular. Qwen/Qwen3.8-27B-FP8 o listede yoktur ve
listeyi imajın içinde yamalamak bir sonraki sürüm yükseltmesinde sessizce kırılır. Bunun
yerine araya bir LiteLLM köprüsü koyuyoruz: köprü gpt-4o-mini adını yayınlar, arkasında
Qwen çalışır. Plane'in kodu hiç değişmez.
2. Adres ayarı yok. Plane istemciyi OpenAI(api_key=...) diye kurar, adres vermez. Ama
openai SDK'sı (1.63.x) adresi OPENAI_BASE_URL ortam değişkeninden okur — üstteki tablodaki
satır tam olarak bunun için.
3. max_tokens. Plane isteğe yalnızca model ve messages koyar. Varsayılanı köprü
verir.
plane-llm pod'u — ghcr.io/berriai/litellm:v1.90.2, port 4000, küme içi:
model_list:
- model_name: gpt-4o-mini # Plane'in beyaz listesindeki bir ad
litellm_params:
model: hosted_vllm/Qwen/Qwen3.8-27B-FP8
api_base: https://kaptan.isnet.net.tr/api/v1/inference/v1
api_key: <Kaptan inference anahtarınız>
max_parallel_requests: 4
max_tokens: 64000
# Qwen'in düşünce bloğu aksi hâlde <think>… olarak cevabın içinde gelir ve
# Plane bunu açıklamaya olduğu gibi yazar.
extra_body: {"chat_template_kwargs": {"enable_thinking": false}}
litellm_settings:
drop_params: true
request_timeout: 600
json_logs: trueKomutu açıkça vermek gerekir, imaj kendi başına config dosyasını almaz:
sh -lc "exec litellm --config /app/proxy_server_config.yaml --port 4000 --num_workers 2"Anahtarı Plane'e tanıtın
Yönetim paneline gidin: /god-mode/ → Artificial Intelligence. LLM Model alanına
gpt-4o-mini, API key alanına köprünün LITELLM_MASTER_KEY değerini yazın ve kaydedin.

Bu üç ayar (LLM_MODEL, LLM_PROVIDER, LLM_API_KEY) veritabanında tutulur, ortam
değişkeninden okunmaz — Plane'de SKIP_ENV_VAR=True olduğu için get_configuration_value
önce InstanceConfiguration tablosuna bakar. Pod'a LLM_API_KEY yazmanın hiçbir etkisi
olmaz. OPENAI_BASE_URL ise tam tersi: hiçbir ayar tablosunda yoktur, yalnızca ortamdan
okunur.
Deneyin
Yeni iş kalemi açın. Açıklama kutusunun sağ alt köşesinde AI düğmesi vardır; başlığı yazdığınızda yanına I'm feeling lucky eklenir.

I'm feeling lucky başlığı alır ve açıklamayı üretir — aşağıdaki metnin tamamı kurum içindeki Qwen3.8'den gelmiştir:

Tuzaklar
1 — İmaj kendi ortam değişkeninizi her açılışta eziyor. /app/start.sh, DOMAIN_NAME
değerinden CORS_ALLOWED_ORIGINS'i yeniden kurar (update_env_value "CORS_ALLOWED_ORIGINS" "http://$DOMAIN_NAME,https://$DOMAIN_NAME"). Pod'a ne yazarsanız yazın, konteyner açılırken
üzerine yazılır. Plane bu listeyi CSRF_TRUSTED_ORIGINS'e de verdiği için sonuç her form
gönderiminde CSRF Verification Failed olur. Çözüm, açılış betiğine dokunan bir sarmalayıcı:
o tek satırı genişletip start.sh'i öyle çalıştırmak.
Sarmalayıcıyı pod komutuna satır içi yazmayın — tırnaklar bozulur ve konteyner
unexpected EOF ile döngüye girer. Dosya olarak bağlayın ve sed'i önce yerelde
bash -n ile sınayın.
2 — Relay Origin'i pod'un kendi adıyla değiştirir. Zorundadır: access-service isteği
X-Forwarded-Host ile yönlendirir. Django tarafında ise SECURE_PROXY_SSL_HEADER
ayarlanmamıştır, yani Plane relay'in arkasında kendini düz http üzerinde sanır ve listede
yazmayan bir https Origin'i reddeder. Pod'un adı <isim>-<pod id'sinin ilk 8 hanesi>
biçimindedir — yani pod var olmadan bilinemez. Bu yüzden joker: https://*.kaptan.isnet.net.tr
hem bugünkü pod'u hem de yeniden oluşturduğunuzda doğacak yeni adı kapsar.
3 — DOMAIN_NAME her şeyi türetir. Yalnızca APP_DOMAIN verirseniz konteyner açılmaz.
AWS_REGION de boş bırakılamaz; MinIO değeri kullanmasa bile açılış kontrolü yalnızca
"dolu mu" diye bakar.
4 — Pod'un ortam değişkeni yerinde düzenlenemez. Platformda pod env'ini değiştiren bir uç
yoktur; OPENAI_BASE_URL gibi bir anahtarı sonradan eklemek pod'u yeniden oluşturmayı
gerektirir. Plane'in verisi plane-db'de durduğu için kayıp olmaz, ama pod adı — dolayısıyla
erişim bağlantısı — değişir. Kurulumu tek bir betikte tutun ki yeniden oluşturmak ucuz olsun.
5 — max_tokens göndermeyen istemciler. Plane isteğe max_tokens koymaz. Bu alan OpenAI
API'sinde isteğe bağlıdır, ama bir uçtan uca zincirde herhangi bir katman onu zorunlu tutuyorsa
istek 400 döner ve belirti yanıltıcıdır: aynı gövde max_tokens ile 200, temperature ile
400. Köprüde varsayılan vermek hem Plane'i hem de aynı eksiği taşıyan diğer istemcileri
(LangChain, LlamaIndex) kurtarır.
İşleri yöneten ajan
Buraya kadarki AI metin üretiyor. "İşleri benim yerime yönet" diyebildiğiniz bir asistan istiyorsanız Plane'in resmî MCP sunucusu vardır: bir ajana projeleri, iş kalemlerini, cycle'ları okuma ve yazma yeteneği verir. Community Edition'da OAuth bulunmadığı için stdio kipinde, kişisel erişim jetonuyla çalışır.
Yedinci pod — plane-agent — bunu kuruyor:
Claude Code + MCP sunucusu, aynı
plane-llm köprüsü üzerinden aynı Qwen3.8'e bağlı.
Jeton
Plane'de Workspace Settings → API tokens altından bir kişisel erişim jetonu üretin
(plane_api_…). Jeton yalnızca ait olduğu çalışma alanında geçerlidir.
Ajan pod'u
ubuntu:24.04, dışarı açılmaz. Açılış betiği Claude Code'un ELF ikilisini ve uv'yi kurar,
ayrıcalıksız bir dev kullanıcısı açar ve MCP tanımını yazar. Ortam değişkenleri:
| Anahtar | Değer |
|---|---|
ANTHROPIC_BASE_URL | http://plane-llm-svc.<ns>.svc.cluster.local:4000 |
ANTHROPIC_AUTH_TOKEN | köprünün LITELLM_MASTER_KEY değeri |
ANTHROPIC_MODEL / ANTHROPIC_SMALL_FAST_MODEL | claude-sonnet-4-5 |
MAX_THINKING_TOKENS | 0 |
CLAUDE_CODE_MAX_OUTPUT_TOKENS | 16000 |
PLANE_API_KEY / PLANE_WORKSPACE_SLUG | jeton ve çalışma alanı kısa adı |
PLANE_BASE_URL | http://plane-app-svc.<ns>.svc.cluster.local |
Köprüye ikinci bir kılık ekleyin — Plane gpt-4o-mini istiyordu, Claude Code Anthropic
biçimini konuşuyor ve kendi tablosunda olmayan bir ad gönderilirse hiç çıktı üretmiyor:
- model_name: claude-sonnet-4-5
litellm_params:
model: hosted_vllm/Qwen/Qwen3.8-27B-FP8
api_base: https://kaptan.isnet.net.tr/api/v1/inference/v1
api_key: <Kaptan inference anahtarınız>
max_tokens: 64000
extra_body: {"chat_template_kwargs": {"enable_thinking": false}}MCP tanımı kullanıcı kapsamında (/home/dev/.claude.json) durmalıdır; proje kapsamlı
bir .mcp.json her oturumda onay ister:
{
"mcpServers": {
"plane": {
"command": "uvx",
"args": ["plane-mcp-server", "stdio"],
"env": {
"PLANE_API_KEY": "plane_api_…",
"PLANE_WORKSPACE_SLUG": "kaptan-ekip",
"PLANE_BASE_URL": "http://plane-app-svc.<ns>.svc.cluster.local"
}
}
}
}Çalıştırın
Pod'un terminalini açın, su - dev deyip claude yazın. Bağlantıyı önce doğrulayın:
claude mcp list
# plane: uvx plane-mcp-server stdio - ✔ ConnectedSonra iş verin — "Gao Kaptan Platform projesinde şu başlıkla bir iş kalemi aç ve açıklamasını yaz". Aşağıdaki KPT-4 kaydını ajan açtı:

6 — su - dev ortamı siler. Login kabuğu ortamı sıfırdan kurar; konteynerde tanımlı her
ANTHROPIC_* değişkeni kaybolur ve Claude Code "Not logged in" der. Açılış betiği bunları
/etc/profile.d/ altına yeniden yazmalıdır.
7 — MCP sunucusu Python'dur, Node değil. uvx plane-mcp-server stdio ile çalışır ve
Python 3.10+ ister; pod'a uv kurmak gerekir. npx arayan bir yapılandırma çalışmaz.
8 — İstemi argüman olarak geçirmeyin. Betikten claude --print "…" çağırırken tırnaklar
birkaç kabuk katmanından geçer; istem yolda yutulduğunda hata mesajı yanıltıcıdır
("Input must be provided either through stdin or as a prompt argument") ve komut başarıyla
(0) biter. İstemi bir dosyaya yazıp stdin'den verin.
Multica: kendi modelinizle çalışan ajan ekibi
Açık kaynak proje yönetimini Kaptan'a kurun, ajanlarını kendi Qwen'inize bağlayın ve bir işi baştan sona bir ekibe yaptırın.
Paperclip: ajanlardan bir şirket, kendi modelinizle
Paperclip'i Kaptan'a kurun, ajanlarını Claude Code üzerinden kurum içindeki Qwen3.8'e bağlayın. İş atayın, ajan çalışsın, çıktıyı panodan izleyin.

Gao Kaptan Docs