Lewati ke konten

Jalankan server headless

Jalankan cc_server di mesin yang tetap menyala, lalu hubungkan ke sana dari desktop, peramban, atau ponsel Anda. Server ini adalah biner native Dart murni — tanpa mesin Flutter — sehingga berjalan headless di macOS, Linux, atau Windows. Server ini memiliki basis data, menjalankan agen, dan melakukan setiap panggilan API eksternal; klien hanya merender.

Setiap rilis menerbitkan stack yang dihosting sendiri berisi empat image GHCR — backend, dua klien tipis statis, dan relay pairing opsional — plus image cc-server-demo terpisah untuk host demo publik. Instalasi yang dihosting sendiri tidak memerlukan checkout sumber. Pin tag versi (ghcr.io/samuelalev/<image>:v0.0.1-rc.1) atau pakai latest.

Image Apa itu Memublikasikan
ghcr.io/samuelalev/cc-server Backend: basis data, agen, MCP, webhook -p 9030:9030
ghcr.io/samuelalev/cc-webapp Klien web (nginx statis) -p 8080:8080
ghcr.io/samuelalev/cc-remote PWA pendamping ponsel (nginx statis) -p 8081:8080
ghcr.io/samuelalev/cc-signaling-server Relay pairing WebRTC (opsional) -p 8788:8788

Seluruh stack sebagai file compose:

services:
server:
image: ghcr.io/samuelalev/cc-server:latest
ports: ["9030:9030"]
volumes: ["cc_data:/data"]
restart: unless-stopped
web:
image: ghcr.io/samuelalev/cc-webapp:latest
ports: ["8080:8080"]
restart: unless-stopped
remote:
image: ghcr.io/samuelalev/cc-remote:latest
ports: ["8081:8080"]
restart: unless-stopped
signaling:
image: ghcr.io/samuelalev/cc-signaling-server:latest
ports: ["8788:8788"]
restart: unless-stopped
volumes:
cc_data:

Hal-hal yang perlu diketahui sebelum Anda mengekspos semua ini di luar loopback:

  • Image cc-server hadir dengan CC_SERVER_INSECURE=1 (image ini harus mengikat 0.0.0.0 untuk menerima lalu lintas dari port yang dipublikasikan), yang hanya aman di balik reverse proxy terminasi TLS — Caddy, Traefik, nginx, atau Cloudflare Tunnel — di jaringan tepercaya. Untuk menerminasi TLS di dalam proses sebagai gantinya, pasang sertifikat dan setel CC_SERVER_TLS_CERT / CC_SERVER_TLS_KEY (lalu hapus setelan CC_SERVER_INSECURE). Semuanya dapat ditimpa dengan docker run -e … atau blok environment: di compose.

  • /data adalah satu-satunya state. Basis data SQLite, rahasia perangkat yang dipasangkan, model yang diunduh, dan audio rapat semuanya berada di volume — cadangkan volumenya dan kontainer menjadi dapat dibuang.

  • Pasangkan klien dari dalam image sebelum koneksi pertama, padanan kontainer dari langkah 3:

    Jendela terminal
    docker run --rm -it -v cc_data:/data \
    --entrypoint /app/bin/cc_server ghcr.io/samuelalev/cc-server:latest pair
  • Kedua image klien adalah host nginx statis — tidak ada yang perlu dikonfigurasi saat runtime; formulir koneksi setiap klien menerima URL server, id perangkat, dan kunci pairing Anda. Relay pensinyalan juga hanya memerlukan portnya (SIGNALING_HOST / SIGNALING_PORT dapat ditimpa); relay ini tanpa status, tidak menyimpan rahasia, dan merupakan cadangan — ponsel memakai loopback, LAN, tailnet, atau jalur wss:// langsung bila salah satunya dapat dijangkau, dan hanya beralih ke relay bila tidak ada. Lewati relay ini jika klien Anda selalu menjangkau server secara langsung.

Sisa panduan ini membangun server dari sumber; flag dan perilaku yang dibahasnya sama dengan yang diumpankan oleh variabel lingkungan image tersebut.

  • Dart SDK. Repo ini mematok SDK-nya dengan fvm, jadi awali setiap perintah dengan fvm.
  • Repositori ini sudah di-checkout.
  • Toolchain native yang dibutuhkan skrip staging — Rust/cargo, plus meson, ninja, pkg-config, git, dan kompilator C++. Setiap skrip build menyebutkan apa yang tidak dapat ditemukannya dan cara memasangnya.
  • Host yang dapat dijangkau: loopback untuk penggunaan lokal, atau alamat LAN/publik dengan TLS untuk klien jarak jauh.

Pustaka native ini wajib — tidak ada mode terdegradasi. Bangun pustaka tersebut dari akar repo sebelum hal lainnya:

Jendela terminal
scripts/natives/build_natives.sh

Perintah ini membangun rift, fff, tree-sitter beserta tata bahasanya, aec, lame, ccpty, cc_watcher, cc_inference, dan native SAML ke dalam build/natives/. Hook build cc_server memancarkan ulang apa pun yang di-stage di sana ke dalam bundle, jadi melewati langkah ini entah menggagalkan build secara langsung atau — dengan jalan keluar di bawah — menghasilkan biner yang preflight boot-nya menolak untuk mulai dan menyebutkan pustaka yang hilang.

Jendela terminal
cd apps/cc_server
fvm dart build cli

Bundle menyertakan libsqlite3 dan pustaka native yang sudah di-stage bersama biner — tidak perlu SQLite sistem maupun mesin Flutter. Executable-nya berada di ./build/cli/<os_arch>/bundle/bin/cc_server.

Langkah 3: Pasangkan klien sebelum mulai pertama kali

Bagian berjudul “Langkah 3: Pasangkan klien sebelum mulai pertama kali”

Direktori data yang baru tidak memiliki perangkat yang dipasangkan dan tidak memiliki kunci pre-shared, sehingga permintaan kunci pairing dari klien belum dapat dipenuhi. Buat satu dengan subcommand pair. Perintah ini menulis global.db dan secrets.json secara langsung; server yang sudah menyajikan direktori data itu akan mengenali perangkat baru tanpa perlu dimulai ulang:

Jendela terminal
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030

Perintah ini mencetak tiga nilai untuk ditempelkan ke formulir koneksi klien:

Connect a thin client (paste into its connect form):
Server ws://localhost:9030/rpc
Device id web-client
Pairing key <a freshly generated key>

Flag yang berguna: --device (bawaan web-client; id ini juga menentukan tingkat perangkat — desktop, ios, android, selain itu dianggap klien web), --label (bawaannya nama platform plus hostname mesin ini), --host (host yang dicetak di URL, karena loopback tidak dapat dijangkau dari mesin lain) dan --client-url. Menjalankan ulang pair memutar kunci perangkat itu dan memutus sesi aktifnya. Pemasangan tidak pernah membuat ruang kerja: onboarding klien yang terhubung yang menamai dan membuat ruang kerja pertama.

Dengan --client-url, perintah ini juga mencetak QR yang dapat dipindai dari terminal dan membuka klien web yang sudah terisi:

Jendela terminal
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030 \
--bind any --host 192.168.1.42 --client-url https://app.usectrl.dev

Kredensial yang dibuat oleh cc_server pair tidak memiliki masa kedaluwarsa; kredensial yang dibuat di aplikasi selalu kedaluwarsa setelah 30 hari. Cabut perangkat yang dibuat melalui CLI dari Pengaturan → Anda → Perangkat Anda setelah selesai menggunakannya.

Di loopback — bawaan dan semua yang Anda perlukan untuk klien di mesin yang sama:

Jendela terminal
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030

Untuk menjangkaunya dari mesin lain, ikat semua antarmuka. Pengikatan non-loopback tanpa TLS ditolak saat startup (Refusing to bind non-loopback address … without TLS), jadi sediakan sertifikat:

Jendela terminal
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \
--bind any --tls-cert /path/fullchain.pem --tls-key /path/privkey.pem \
--public-url wss://server.example:9030/rpc

Atau terminasi TLS di reverse proxy pada jaringan privat tepercaya dan pilih ikut serta dalam pengikatan plaintext di baliknya:

Jendela terminal
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \
--bind any --insecure --public-url wss://server.example/rpc

--insecure diabaikan bila --tls-cert dan --tls-key keduanya disetel — TLS selalu menang. Server mengiklankan status plaintext sebagai insecure: true di GET /healthz dan klien menandai koneksi semacam itu sebagai tidak aman.

Server berjalan sampai SIGINT/SIGTERM, lalu mati dengan bersih.

Arahkan klien ke URL server dan kunci pairing dari langkah 3:

  • Aplikasi desktop: di layar pertama kali dijalankan (Bagaimana Control Center harus dijalankan?), isi kolom koneksi alih-alih Jalankan di aplikasi ini, atau tambahkan server nanti di Pengaturan → Server → Koneksi & status. Lihat Hubungkan ke server jarak jauh.
  • Klien web: buka app.usectrl.dev (atau salinan build web Anda sendiri) dan masukkan URL server, id perangkat, dan kunci pairing.
  • Ponsel: pasangkan aplikasi Remote dari klien yang sedang berjalan — lihat Pasangkan perangkat.

Langkah 6: Hubungkan Google Calendar (opsional)

Bagian berjudul “Langkah 6: Hubungkan Google Calendar (opsional)”

Untuk menyinkronkan kalender ruang kerja tanpa GUI terpasang, gunakan alur kode perangkat:

Jendela terminal
GOOGLE_OAUTH_CLIENT_ID=<id> GOOGLE_OAUTH_CLIENT_SECRET=<secret> \
./build/cli/<os_arch>/bundle/bin/cc_server calendar connect \
--data-dir ./data --workspace <workspace-id>

Perintah ini mencetak kode dan URL untuk disetujui di perangkat lain, menyimpan token penyegaran di sisi server, lalu keluar. Build dari sumber tidak menyertakan klien Google, jadi variabel lingkungan id dan secret wajib diisi — tidak ada flag --google-client-id. Lihat Hubungkan Google Calendar.

Flag yang digunakan di atas adalah yang dibutuhkan penyiapan pertama. Permukaan lengkapnya — 23 pasangan flag dan variabel lingkungan, empat subcommand, dan tata letak direktori data — ada di referensi CLI cc_server.

Empat yang paling mungkin Anda butuhkan:

Flag Bawaan Arti
--data-dir direktori data aplikasi per pengguna OS Basis data, rahasia, model, dan media yang di-cache
--port 9030 Port TCP (0 = sementara)
--bind loopback loopback, atau any untuk semua antarmuka (perlu TLS atau --insecure)
--public-url diturunkan dari pengikatan URL RPC yang diiklankan ke klien yang dipasangkan. Setel ini secara eksplisit di balik proxy, NAT, atau tunnel

Direktori data bawaannya adalah ~/Library/Application Support/control-center di macOS, %APPDATA%\control-center di Windows, dan $XDG_DATA_HOME/control-center (atau ~/.local/share/control-center) di tempat lain. .cc_server yang relatif terhadap cwd hanya digunakan bila tidak ada direktori home atau data aplikasi yang dapat ditentukan.

cc_server adalah seluruh permukaan produk, bukan sebagiannya: jalur data dan langganan repo-RPC, endpoint alat MCP di POST /mcp dan GET /sse pada port yang sama, webhook masuk, proksi media dan font, callback single sign-on, protokol sewa fleet, pengambilan RSS sisi server dengan jadwal 30 menit, dan pencarian vektor (ekstensi sqlite_vector didaftarkan sebagai auto-extension global proses, dan hanya terdegradasi ke pencarian teks lengkap di tempat ekstensi itu tidak tersedia).

Dispatcher bersifat stateless: tidak ada pengikatan ruang kerja per sesi. Setiap panggilan berlingkup ruang kerja membawa workspace_id-nya sendiri dan akses ditentukan oleh keanggotaan dan peran ruang kerja pemanggil — bukan karena memegang kunci pairing.

Dua hal yang perlu diketahui sebelum Anda mengekspos port di luar loopback:

  • MCP gagal secara tertutup di luar host. Permukaan MCP tanpa token hanya menjawab loopback. Mengikat any tidak melebarkannya — permintaan dari luar host ditolak dengan “MCP requires a bearer token to answer non-loopback clients” sampai Anda mengonfigurasi token (mcp.setToken, atau Pengaturan → Server → Server MCP).
  • GET /healthz tidak diautentikasi secara sengaja, memakai CORS wildcard dan memublikasikan id, nama, sidik jari identitas, dan versi build server. Klien memerlukan sidik jari itu untuk mematok server, jadi ini disengaja — tetapi artinya setiap cc_server yang dapat dijangkau mengungkapkan sebanyak itu kepada siapa pun yang bertanya.