Clepit
Tóm tắt nhanh
- Danh mục
- Nền tảng cho lập trình viên
- Trang web
- clepit.com
- Bảng điều khiển
- app.clepit.com
- Tài liệu
- clepit.com/en/docs
- API GraphQL
- api.clepit.com/graphql
- Thời gian thực
- ws.clepit.com
- Giao diện MCP
- api.clepit.com/mcp
- Trang đã xuất bản
- clepit.space
Phần lớn văn bản có định dạng rốt cuộc nằm trong cơ sở dữ liệu như một khối HTML. Điều đó không sao cả, cho đến khi bạn muốn làm gì đó khác ngoài việc hiển thị lại: tìm mọi trang có nhắc đến một khách hàng, đưa cùng một tài liệu ra trang web, thư điện tử và ứng dụng di động, hoặc để hai người cùng sửa mà không ai mất một đoạn. Đến lúc ấy, con chữ và hình thức của nó đã quấn vào nhau, và thứ duy nhất còn đọc được tài liệu một cách chắc chắn là chính trình soạn thảo đã viết ra nó.
Clepit tách hai thứ đó ra. Một trang là danh sách các khối, mỗi khối là một đối tượng nhỏ có kiểu, còn tài liệu chính là JSON: không có đánh dấu nào phải phân tích, cũng không cần trình soạn thảo mới đọc được. Đó cũng là lý do trình soạn thảo có thể đứng một mình. @clepit/core được phát hành trên npm theo giấy phép MIT và không biết gì về phía dịch vụ, trong khi không gian làm việc tại app.clepit.com là hình hài của chính những tài liệu ấy khi chúng có thêm người cùng viết, phân quyền, lịch sử phiên bản và một địa chỉ trên web công khai.
Một trang là danh sách các khối
Một khối gồm bốn trường: một id, một kiểu, dữ liệu mà kiểu ấy quy định, và những tinh chỉnh đã áp lên nó. Dữ liệu của một đoạn văn có hình dạng khác với dữ liệu của một bảng, và chính cái kiểu cho biết nên chờ đợi hình dạng nào, nhờ vậy tài liệu đã lưu có thể được kiểm chứng chứ không chỉ được phân tích. Cả một trang là một dấu thời gian, một phiên bản và các khối theo thứ tự, đủ nhỏ để đọc bằng mắt thường và soi lại trong một pull request. Không có gì trong đó mô tả trang phải trông ra sao: điều ấy thuộc về thứ đang vẽ nó, và đó là lý do một tài liệu có thể trở thành trang web, thư điện tử và màn hình điện thoại mà không cần tồn tại ba bản sao.
Vẽ một tài liệu mà không cần trình duyệt
Bộ kết xuất không bao giờ ghi thẳng vào trình duyệt. Nó vẽ qua một lớp mỏng, phía sau có hai nền tựa: một nền dựng các nút trang thật, một nền dựng chuỗi ký tự, và cùng một đoạn mã khối chạy trên cả hai. Đó là cách một trang đã xuất bản được vẽ trên máy chủ nơi chẳng có trình duyệt nào, và cũng là lý do thứ máy chủ tạo ra chính là tài liệu mà trình soạn thảo lẽ ra đã hiển thị, chứ không phải một bản cài đặt thứ hai bị bỏ mặc cho lệch dần. Nền chuỗi đòi một bộ làm sạch như tham số bắt buộc và cố ý không có giá trị mặc định: bộ làm sạch quen thuộc của gói phải dựng một trang mới phân tích được, nên không thể chạy ở đó, còn việc lặng lẽ lui về thoát ký tự sẽ tước mất định dạng bên trong của mọi tài liệu mà không nói một lời. Vì vậy khoảng trống ấy được để lộ ngay tại nơi sử dụng, thay vì giấu trong một giá trị mặc định.
Đọc không tốn của người đọc chút JavaScript nào
Bộ chuyển đổi React phát hành hai nửa của nó riêng rẽ, bởi chúng cần những điều trái ngược nhau. Thành phần nội dung chạy trên máy chủ và xuất ra đánh dấu hoàn chỉnh ngay trong lúc trang đang được dựng, nên người đọc nhận được tài liệu ngay ở phản hồi đầu tiên. Thành phần soạn thảo thì chỉ chạy phía máy khách, vì nó nắm vòng đời của trình soạn thảo, mà chưa có trình duyệt thì chẳng có gì để nắm. Vì vậy đọc một tài liệu Clepit không cần chút JavaScript nào. Chỉ khi viết, môi trường chạy mới xuất hiện.
Một trang có thể chứa những gì
Gói đi kèm hai mươi sáu loại khối. Phần lớn là những thứ trình soạn thảo nào cũng cần: tiêu đề, đoạn văn, danh sách, danh sách kiểm, trích dẫn, mã, bảng, hình ảnh, âm thanh, video, tệp, hộp lưu ý và đường phân cách. Số còn lại tồn tại vì việc viết tài liệu đòi hỏi những thứ mà một công cụ viết lách thường bỏ qua. Một mục lục tự dựng lấy từ các tiêu đề của tài liệu và liên kết tới từng mục. Các phần gập được và các cột. Một thẻ đứng thay cho một trang khác. Một bản phác tay. Và một khối hoạt động lưu lại việc phải theo dõi tài liệu nào, chứ không lưu bản sao hoạt động của tài liệu ấy, nhờ vậy nó tiếp tục cho thấy điều đang diễn ra thay vì đóng băng ở ngày được chèn vào. Định dạng bên trong một khối gồm những dấu quen thuộc: đậm, nghiêng, gạch chân, gạch ngang, mã trong dòng, tô sáng và liên kết, cùng với chú giải, nhãn trạng thái và nhắc tên. Bản thân gói không có bất kỳ phụ thuộc nào lúc chạy.
Công thức và sơ đồ, vẽ ngay trong gói
Hai trong số các khối ấy hiển thị LaTeX và Mermaid, và cả hai đều làm trọn công việc ngay trong gói: phân tích mã nguồn, tính toán bố cục, vẽ ra kết quả. Bên dưới không có thư viện vẽ nào, cũng không có lời gọi tới dịch vụ nào để biến một công thức hay một lưu đồ thành hình ảnh. Điều đó không hẳn là một sở thích về phụ thuộc, mà là hệ quả của nền chuỗi. Một khối với tay sang thứ chỉ trình duyệt mới có, hoặc với tay ra mạng, sẽ không thể được vẽ trên máy chủ phục vụ các trang đã xuất bản, và khi ấy cùng một trang sẽ trông khác nhau tùy vào ai là người yêu cầu.
Một tài liệu tra cứu API sống ngay trong trang
Hãy đưa cho khối OpenAPI một bản đặc tả, dán vào hoặc trỏ tới bằng địa chỉ, và nó sẽ vẽ ra những gì bản đặc tả mô tả: các thao tác, đường dẫn và tham số của chúng, lược đồ yêu cầu và phản hồi, cùng cách xác thực. Với mỗi thao tác, nó còn dựng một đoạn mã yêu cầu bằng cURL, TypeScript, Dart và Python, sinh ra từ chính bản đặc tả chứ không phải do một tác giả gõ tay rồi quên cập nhật. Khối nhúng nhìn thế giới bên ngoài y như vậy: nó nhận ra một nhúm dịch vụ mà nó thực sự hiển thị được, với tất cả những thứ còn lại thì coi một liên kết thường là kết quả đúng đắn chứ không phải một thất bại, và thẳng thừng từ chối vẽ một địa chỉ mà nó không tin tưởng.
Hai người trong cùng một đoạn văn
Một trang đang được sửa trực tiếp do một tác vụ duy nhất trên máy chủ nắm giữ, mỗi trang một tác vụ, và mọi cập nhật đều đi qua đó theo thứ tự. Chính điều đó khiến việc sửa đồng thời vẫn có thể lần ra đầu đuôi: không có người ghi thứ hai chạy đua với người thứ nhất. Bản thân tài liệu là một CRDT, nên hai người gõ trong cùng một đoạn văn sẽ hòa vào nhau chứ không ghi đè lên nhau, còn một máy khách bị tụt lại thì bắt kịp bằng cách trao đổi những gì mỗi bên còn thiếu. Mỗi cập nhật được ghi thêm vào nhật ký ghi trước khi được phát cho bất kỳ ai, nên thứ mà những người khác trong tài liệu nhìn thấy đã được lưu bền vững chứ không chỉ được chuyển tiếp. Quyền được thực thi ở máy chủ chứ không phải ở giao diện: một người tham gia mà không có quyền sửa sẽ bị hạ xuống chỉ đọc, và phiên làm việc kiểm tra lại quyền ấy theo định kỳ trong lúc tài liệu còn mở, nên quyền bị thu hồi sẽ rơi đúng vào người đang gõ thay vì phải đợi họ tải lại trang.
Mọi lượt ghi đều đi qua một cánh cửa
Một tài liệu có thể bị thay đổi bởi người đang gõ trong đó và bởi một chương trình gọi API, và trước đây hai lối ấy có thể ghi vào cùng một trang một cách độc lập. Nay thì không còn nữa. Lượt ghi từ API được chuyển tới đúng phiên đang giữ tài liệu sống, nơi nó được áp dụng như một giao dịch duy nhất bên cạnh các chỉnh sửa đang diễn ra, nên chỉ có một trật tự sự kiện chứ không phải hai người ghi với hai ý kiến khác nhau về nội dung trang. Các id của khối được giữ nguyên khi tài liệu được ghi trở lại, bởi các bình luận neo vào chúng, và một lần đối chiếu sinh ra id mới sẽ để mọi bình luận trỏ vào hư không. Và khi tập khối thu được giống hệt tập đã lưu, thì không có gì được ghi cả.
Kênh duy nhất mà khóa máy không với tới được
Một khóa API cá nhân dùng được với REST, GraphQL, socket đăng ký của GraphQL và MCP. Nó không dùng được với socket cộng tác, và điều đó là cố ý. Mỗi cập nhật cộng tác đều được đóng dấu tên người đã thực hiện, và những dấu ấy trở thành quyền tác giả được ghi trong lịch sử của trang. Một chủ thể máy sửa ở đó sẽ ghi vào một tác giả mà không người nào viết, và việc hoàn tác sau này nghĩa là viết lại lịch sử chứ không phải xóa một dòng. Ranh giới không nằm giữa websocket và HTTP: nó nằm ở chỗ kênh ấy có ghi lịch sử kèm tác giả hay không. Quy tắc này được chính hình hài của mã bảo đảm chứ không nhờ trí nhớ, bởi muốn chấp nhận một khóa thì phải cố ý chuyển sang một lời gọi xác thực khác, và sẽ có một bài kiểm thử thất bại khi một kênh làm vậy.
Một không gian làm việc trên địa chỉ của riêng nó
Mỗi không gian làm việc là một tenant với tên miền phụ của riêng nó ngay từ lúc được tạo, và tenant được xác định từ chính địa chỉ mà yêu cầu đi tới. Vì thế bạn đang ở tenant nào đã được định đoạt trước khi bất kỳ dữ liệu nào của bạn được đọc, chứ không phải bằng một bộ lọc gắn thêm về sau mà ai đó có thể quên. Bên dưới nữa, chính cơ sở dữ liệu giữ lấy ranh giới ấy bằng bảo mật ở mức hàng: mỗi yêu cầu mượn một kết nối, đóng danh tính của người gọi lên đó, và khi kết nối được trả về thì bể kết nối xóa sạch trạng thái ấy, nên danh tính của yêu cầu này không thể rò sang các truy vấn của yêu cầu kế tiếp.
Nhận một tên miền không đồng nghĩa với chứng minh nó
Một không gian làm việc thuộc gói Enterprise có thể phục vụ các trang của mình từ một tên miền riêng. Việc nhận một tên miền và việc phục vụ từ tên miền ấy được tách thành hai bước một cách có chủ ý: tên miền được lưu ở trạng thái chưa xác minh, và bộ phân giải hoàn toàn bỏ qua nó cho tới khi một bản ghi xác minh xuất hiện trong DNS. Ai cũng có thể gõ địa chỉ của một công ty khác vào một biểu mẫu. Chỉ người thực sự nắm quyền với tên miền đó mới công bố được bản ghi khiến nó có hiệu lực.
Mọi phiên bản mà trang từng có
Clepit giữ lại các bản sửa đổi chứ không chỉ một trạng thái hiện tại. Ảnh chụp được lấy tự động trong lúc mọi người làm việc, có tiết chế để việc gõ phím thông thường không đẻ ra hàng trăm bản: một bản mới được ghi khi đã qua mười phút hoặc đã có mười khối thay đổi, cái nào đến trước. Khôi phục là một giao dịch duy nhất: ảnh chụp cũ được áp dụng, các khối được đối chiếu, và bản thân lần khôi phục cũng được ghi thành một bản sửa đổi mới, nên việc lùi lại được ghi nhận chứ không bị âm thầm xóa đi. Việc đối chiếu cố ý giữ nguyên id khối của bản nguồn, bởi các bình luận neo vào khối, và khôi phục một trang với id mới sẽ khiến mọi bình luận trên đó mất chỗ neo.
Tìm lại nó
Tìm kiếm chạy trên một phép chiếu của các trang, và câu truy vấn đi qua chính bộ phân tích tìm kiếm web của Postgres thay vì được ghép tay thành SQL, nên người ta có thể gõ dấu ngoặc kép và dấu trừ mà không có thứ nào trở thành khe hở cho tấn công chèn mã. Nhưng điều đáng kể trong một không gian làm việc chung là chỗ đứng của bước kiểm tra quyền. Tìm kiếm nối vào bảng các trang, và bảo mật ở mức hàng của bảng ấy áp dụng ngay trên phép nối, nên kết quả vốn đã bị giới hạn trong những trang mà người hỏi được phép thấy. Các bộ lọc, tức một nhánh của cây trang, ai là người sửa gần nhất, lần sửa cuối là khi nào, được chồng lên đó như các điều kiện bổ sung. Mỗi bộ lọc đều thu hẹp lại; không bộ nào có thể nới rộng ra, bởi tất cả đều nằm sau cùng một bước kiểm tra.
Xuất bản thì đóng băng, chia sẻ thì không
Đây là hai việc khác nhau và Clepit cố ý đối xử với chúng khác nhau. Xuất bản một trang sẽ đóng băng tài liệu hiện thời thành một bản sửa đổi, trỏ trang vào đó, và làm nó công khai: thứ mà khách đọc trên clepit.space, tại một địa chỉ kiểu acme.clepit.space/handbook, chính là bản đã đóng băng ấy chứ không phải những chỉnh sửa về sau. Hủy xuất bản sẽ xóa các con trỏ đó nhưng giữ lại địa chỉ công khai, nên xuất bản lại về sau sẽ quay về đúng URL cũ thay vì làm gãy mọi liên kết từng trỏ tới đó. Liên kết chia sẻ thì ngược lại: nó phục vụ tài liệu sống, nên thứ người nhận thấy sẽ đổi theo trang.
Một liên kết bạn có thể thu hồi
Liên kết chia sẻ là một mã bạn có thể thu hồi, và khi tạo ra có thể gán cho nó thời hạn. Chỉ có bản băm của mã ấy được lưu, nên liên kết chỉ hiện một lần lúc tạo và sau đó không thể khôi phục từ cơ sở dữ liệu, kể cả bởi chúng tôi lẫn bởi bất kỳ ai chạm tới nó. Những liên kết này cấp quyền đọc chứ không cấp quyền bình luận: một bình luận cần có tác giả, mà người cầm liên kết thì không phải là tác giả.
Đăng nhập từ thư mục của chính bạn
Một không gian làm việc có thể giao việc xác thực cho nhà cung cấp danh tính của chính mình: OpenID Connect ở gói Business, SAML ở gói Enterprise, cùng với SCIM bên cạnh để đồng bộ thư mục. SCIM lo đúng tài nguyên người dùng mà Okta và Entra thực sự vận hành, và nó cố ý đi chệch một điểm so với cách đọc hiển nhiên của tiêu chuẩn: lệnh xóa sẽ vô hiệu hóa thành viên chứ không xóa sạch họ. Bản đặc tả cho phép điều đó, còn lựa chọn ngược lại là một lần đồng bộ thư mục có thể hủy hoại nội dung của cả một không gian làm việc chỉ vì ai đó bị đưa ra khỏi một nhóm.
Bốn cách để trò chuyện với nó
REST bao gồm bốn mươi bốn thao tác dưới /v1, được mô tả bằng một tài liệu OpenAPI sinh ra từ chính các tuyến đường chứ không phải viết tay bên cạnh chúng, và tích hợp liên tục đối chiếu kết quả sinh ra ấy với bản đã lưu trong kho, nên một thay đổi tuyến đường bỏ qua sổ đăng ký không thể lặng lẽ lọt vào. GraphQL bao trùm mô hình ứng dụng và tải các đăng ký qua socket riêng của nó. Thời gian thực là một tiến trình riêng, vì thế khởi động lại cổng không kéo theo cả bề mặt yêu cầu, và nó cấp quyền theo từng socket chứ không theo từng phòng: với mỗi sự kiện, mọi kết nối trong tenant được xét trong một lần kiểm tra gộp, và chỉ những kết nối được phép thấy mới nhận được. MCP mở chính những thao tác ấy cho các tác nhân AI dưới dạng công cụ, và không công cụ nào tin vào một id do bên gọi đưa ra để quyết định mình đang làm việc trong không gian nào; các lượt ghi đi qua đúng những dịch vụ mà giao diện dùng, nên các bước kiểm tra quyền và dấu vết kiểm toán cũng là một.
Dành cho ai
Các nhóm cần một trình soạn thảo do chính họ kiểm soát, và lập trình viên nhúng nội dung có cấu trúc vào sản phẩm của mình.
Truy cập Clepit: clepit.com