Status: Draft Updated: 2026-06-28 Audience: Ourin (Sakurarin) / baseware implementors and client developers Scope: Defines macOS-native (Network.framework + XPC) differences required for safe operation while maintaining the vocabulary and behavior of UKADOC SSTP/1.x. Non-goals: Binary compatibility with Windows WM_COPYDATA. Only vocabulary and behavioral compatibility.
1. Policy and Non-goals
- Wire format (method/headers/CRLF termination) conforms to SSTP/1.x. Interpretation of
SEND/NOTIFY/COMMUNICATE/EXECUTE/GIVE,Charset,Sender,Option, etc. follows the original specification. Keep-alive is not assumed; connections are closed after each exchange. - Transport layer: TCP server implemented with Network.framework (default port 9801/tcp; optional port 9821 for SSP compatibility). Default: loopback only.
- DirectSSTP replacement: Windows WM_COPYDATA-based DirectSSTP is replaced on macOS with XPC (NSXPCConnection) as an equivalent IPC mechanism.
2. Terminology
- (Socket)SSTP: SSTP exchanged over TCP.
- DirectSSTP: Lightweight SSTP using OS IPC. On macOS, this is XPC.
- SSTP over HTTP: Compatibility bridge that carries SSTP request/response as the HTTP message body.
3. Compatibility Policy (unchanged from upstream)
- SSTP overview and port: SSTP is general-purpose ghost-to-ghost communication. Implementation uses 9801/tcp (SSP may also use 9821). 9801 is the primary port in active use. Loopback-only listening is standard.
- Syntax:
CRLFline endings, blank line terminator. Charset header is required (omission causes an error or implementation-defined fallback to OS default). Responses may carry additional data. Request and response may carry different SSTP version numbers, so version-dependent interpretation is prohibited. - Methods:
NOTIFY(event notification),SEND(script delivery),COMMUNICATE(user-input-equivalent response),EXECUTE(status query/control),GIVE(legacy spec) are all retained.
4. macOS Differences (replacements/additions)
4.1 (Socket)SSTP (TCP)
- Implementation: Started with
NWListener(using: .tcp, on: 9801). Default: bind to 127.0.0.1/::1 only. External exposure is opt-in via configuration. - Bind address:
SstpTcpServer.makeListener(host:port:)determines the bind target from thehostparameter. Specific addresses such as127.0.0.1/localhost/::1bind to that address only usingNWParameters.requiredLocalEndpoint. Only0.0.0.0/::/ empty string listens on all interfaces.SstpHttpServeruses the samemakeListenermethod. - TCP body forwarding: After detecting the header terminator (
\r\n\r\n), the TCP handler passes the entire buffer including headers and body to the SSTP stack. The previous implementation discarded the body; sinceSSTPParserreads everything after the blank line as the body, the full buffer must be forwarded. - Sandbox: Under App Sandbox, the server requires
com.apple.security.network.serverand the client requirescom.apple.security.network.client. - Firewall: When the application firewall is enabled, incoming connections must be allowed. Signed/downloaded apps have an automatic allow setting.
4.2 DirectSSTP (macOS XPC version)
- Implementation: An XPC service such as
App.app/Contents/XPCServices/ukagaka.sstp.xpc. - Interface:
executeSSTP(request: Data, withReply: (Data)->Void)(request/response content is SSTP/1.x text). - Connection:
NSXPCConnection(serviceName:)orNSXPCConnection(machServiceName:). Set code signature requirements as needed. - Security: XPC fundamentally requires same-developer code signing. Connections to external processes require Mach service/endpoint design.
4.3 Identifying the receiving ghost
- ReceiverGhostName is recommended. ReceiverGhostHWnd is not applicable on macOS (ignored). (In (Socket)SSTP, a ghost can be targeted by name. If not found, 404 Not Found is returned.)
4.4 SSTP to SHIORI live-ghost bridge
- External SSTP requests now reach the live loaded ghost (YAYA) via
SSTPDispatcher→BridgeToSHIORI.handleResponse→liveGhostResolver→YayaAdapter. Previously, ifSHIORI_BUNDLE_PATHwas not set, no ghost was consulted. BridgeToSHIORI.handleResponseresolves in order: (1) registered test resource, (2) native SHIORI bundle viaShioriHost, (3) live ghost vialiveGhostResolver.liveGhostResolveris a closure of type(String, String, [String], [String: String]) -> BridgeShioriResponse?registered byAppDelegateat startup.- SHIORI method mapping:
| SSTP method | SHIORI method | Reason |
|---|---|---|
NOTIFY |
NOTIFY SHIORI/3.0 |
Notification; no return value expected (UKADOC SHIORI method spec) |
SEND |
GET SHIORI/3.0 |
Script return value expected |
COMMUNICATE |
GET SHIORI/3.0 |
Script return value expected |
EXECUTE |
GET SHIORI/3.0 |
Script return value expected |
GIVE |
GET SHIORI/3.0 |
Script return value expected |
- ReferenceN preservation:
mapShioriResponsecopies everyReference0throughReferenceNfrom the SHIORI response into the SSTP response headers. Per UKADOC, allReferenceNheaders are forwarded to the SSTP caller unchanged. - Multi-line value safety: Ghost responses may contain Value fields with embedded newlines (e.g. Sakura Script).
BridgeShioriResponsekeeps the response structured internally.serializeWirestrips CR/LF from all header values before building the wire string, preventing header injection and script truncation. Sakura Script newlines are represented by the\ntoken, so stripping bare newlines from the wire is display-safe. Synchronous IPC timeout: 2 seconds.
5. Character Encoding Policy
- Default is UTF-8.
- For compatibility,
Shift_JIS/Windows-31J/CP932/SJISand similar labels are accepted as the same family (CP932 ≈ Windows-31J). WHATWG Encoding treatsshift_jisandwindows-31jas the same decoder.
6. Header Differences (accepted/not applicable)
- Preserved:
Sender,SecurityLevel(local/external),SecurityOrigin,Option(nodescript/notranslate/nobreak),ID(Owned SSTP),X-SSTP-PassThru-*. - Not applicable:
HWnd(DirectSSTP-specific) → macOS uses XPC, so it is ignored.ReceiverGhostHWndlikewise. - SEND-specific:
Option: notifyis implemented (equivalent to SSP 2.6.76 behavior).
6.1 Owned SSTP
- An
IDis valid only when it exactly matches the SHIORIuniqueidnotified to the target ghost or that ghost's FMO record ID. - With
ReceiverGhostName, only IDs belonging to that ghost are matched. Without it, only the primary ghost is matched. - A valid Owned request reaches SHIORI as
SecurityLevel: local. Unknown or target-mismatched IDs keep their original security context. - Requests from an external HTTP Origin are not promoted to Owned even when the ID matches.
7. Status Codes
- 200 OK (with return value) / 204 No Content (no return value) / 210 Break (executed but break) / 400 / 404 / 408 / 409 / 413 / 420 / 500 / 501 / 503 / 505 / 512 are all maintained.
8. SSTP over HTTP (compatibility)
- Endpoint:
POST http://localhost:9801/api/sstp/v1,Content-Type: text/plain, Content-Length required. - Response: HTTP always returns 200 OK (body is the SSTP response). Origin other than localhost is treated as external unconditionally.
9. Examples (SEND/NOTIFY/EXECUTE)
9.1 SEND (minimal)
SEND SSTP/1.0
Charset: UTF-8
Sender: Ourin
Script: \h\s0Hello
Response
SSTP/1.4 200 OK
Charset: UTF-8
Script: \h\s0Received!
9.2 NOTIFY (event notification only)
NOTIFY SSTP/1.0
Charset: UTF-8
Sender: Media Player
Event: OnMusicPlay
Reference0: Wings of Freedom
Reference1: Linked Horizon
9.3 EXECUTE (information query)
EXECUTE SSTP/1.1
Charset: UTF-8
Sender: Ourin
Command: GetName
10. Reference Implementation Parameters (recommended defaults)
- Port: 9801/tcp (optionally also 9821).
- Bind: 127.0.0.1 / ::1 (external exposure is opt-in).
- Charset: Default UTF-8; SJIS-family labels accepted as CP932.
11. Implementation Status
Updated: 2026-06-28
11.1 Ourin host-side implementation
- [x] TCP SSTP server: Implemented in
SstpTcpServer.swiftusing Network.framework - [x] HTTP SSTP server: Implemented in
SstpHttpServer.swift - [x] XPC DirectSSTP: Implemented in
XpcDirectServer.swiftandDirectSSTPXPC.swift - [x] SSTP parser: Implemented in
SSTPParser.swift(order-preserving header support) - [x] SSTP dispatcher: Implemented in
SSTPDispatcher.swift— all methods and major option headers - [x] SSTP↔SHIORI live ghost bridge: Implemented in
BridgeToSHIORI.swiftvialiveGhostResolverrouting to the running ghost (YAYA) - [x] Character encoding: Default UTF-8; Shift_JIS/CP932 acceptance implemented
- [x] Unified management: TCP/HTTP/XPC unified in
OurinExternalServer.swift - [x] Full SSTP/1.x protocol: All methods and major option headers implemented
11.2 Implemented features
-
SocketSSTP (TCP) - TCP server implementation via Network.framework - Listening on port 9801 - Loopback-only reception via
requiredLocalEndpoint(127.0.0.1/::1);0.0.0.0/::listens on all interfaces - All SEND/NOTIFY/COMMUNICATE/EXECUTE/GIVE/INSTALL methods handled - Full request buffer (header + body) forwarded to SSTP stack (no body discarding) -
SSTP over HTTP - HTTP server implementation (shares
SstpTcpServer.makeListener) -/api/sstp/v1endpoint - Accepts requests with Content-Type: text/plain -
DirectSSTP (XPC) - Inter-process communication via NSXPCConnection -
OurinSSTPXPCprotocol implemented -executeSSTP(_:withReply:)method implemented -
Request processing - CRLF + blank-line terminator parsing -
Charsetheader processing -SecurityLevel/SecurityOrigininterpretation andsecurityLocalOnlypolicy - Ghost targeting viaReceiverGhostName(404 if not found) - AllOptionpatterns (nodescript/notranslate/nobreak) -
Routing and SHIORI bridge - All channels unified through
SSTPDispatcher.swift(oldSstpRouterremoved) -BridgeToSHIORI.handleResponsereturns the full SHIORI/3.0 wire response - Reaches YayaAdapter (live YAYA ghost) vialiveGhostResolver- SSTP NOTIFY propagates asNOTIFY SHIORI/3.0; SEND/COMMUNICATE/EXECUTE/GIVE asGET SHIORI/3.0-mapShioriResponsereflects allReference0..ReferenceNinto the SSTP response -
Safe transport of multi-line Value -
BridgeShioriResponsestruct keeps the response structured -serializeWirestrips CR/LF from header values before wire serialization (prevents header injection and script truncation) - Synchronous IPC timeout: 2 seconds
11.3 External exposure configuration
- Loopback-only is the default
- Passing
0.0.0.0/::as thehostparameter enables reception on all interfaces (opt-in)
12. Conformance Checklist
- [x] Correct handling of CRLF and blank-line terminator (implemented)
- [x]
Charsetrequired (UTF-8 recommended); SJIS-family accepted as CP932 (implemented) - [x]
SecurityLevel/SecurityOrigininterpretation (implemented) - [x] Ghost targeting via
ReceiverGhostName(404 if not found) (implemented) - [x] DirectSSTP (macOS) = XPC bridging
request(Data)->Data(implemented) - [x] SSTP over HTTP
/api/sstp/v1implemented with localhost restriction (implemented) - [x] SSTP→SHIORI method mapping (NOTIFY→
NOTIFY SHIORI/3.0, others→GET SHIORI/3.0) (implemented) - [x] All SHIORI response
ReferenceNforwarded to SSTP response (implemented) - [x] TCP/HTTP listeners bind via
requiredLocalEndpointper configuredhost(implemented) - [x] TCP raw path forwards full request (header + body) to SSTP stack (implemented)
- [x] CR/LF stripping from multi-line Value prevents header injection (implemented)
13. Appendix A: Minimum XPC DirectSSTP Interface
@objc public protocol OurinSSTPXPC {
func executeSSTP(_ request: Data, withReply reply: @escaping (Data) -> Void)
}
// Host side: connect via NSXPCConnection(serviceName: "app.ourin.sstp"),
// send the SSTP text (UTF-8) as Data in request.
出典と編集について
Ourin / Furin Lab・contributors。文書はCC BY-SA 4.0(第三者の個別表記を除く)。Web版は見出し・リンクの整備とローカルパスの一般化を行っています。更新基準日: 2026-10-30。
元資料: docs/SSTP_1.xM_SPEC_en-us.md
ソース SHA-256: 958cac9bf72f2264c3dc2a6a8b508a22ce5679250af8818bcc2f9fe7a599ed4b