コンテンツへ移動
KAMOUniverse

チェンジログ

何が新しいの?

プラットフォームのすべての改良 それが出荷される

  1. · kamo-signer-monorepo

    Public branded quote/estimate document at /{locale}/q/{token}

    The customer-facing surface for the commerce quote system. Server-rendered so the org's branding is in the first paint — an unbranded flash on a commercial document is exactly what this must not do, and it is why the page emits brand custom properties inline via QuoteBrandVars rather than swapping them after hydration. Lives in kamo-signer because that app already has the HostRegexp ingress, autocert coverage, DNS aliases and the theme package with hostnameOverride — and because quote -> accept -> e-sign is then one hop, since EsignGenericEnvelope binds any record via (context_type, context_id). Reusing it was only viable once the /{locale}/ i18n work landed; all 31 strings are translation keys sourced from the dictionary. Money and dates format through next-intl using the quote's own currency and the server-resolved timezone (recipient member -> org -> UTC), never the viewer's device. An expiry rendered in the wrong zone can name a date on which the offer is already dead. Adds the platform's first real @media print block. "Print Invoice" in kamo-internal has always printed the whole app chrome because no @media print rule existed anywhere; here the same markup prints as a clean Letter document with the brand accent preserved (print-color-adjust: exact — a colourless quote reads as a draft), the action bar suppressed, and page-break-inside: avoid on rows and totals. Terminal states are deliberately asymmetric: EXPIRED gets an honest branded page naming the date and the org's contact details, safe because the caller already proved possession of a 256-bit token; unknown/revoked/wrong-org all collapse to one generic page so the surface cannot be probed. The client whitelists the reason before rendering. The token never travels onward in a URL — the server-side resolve posts it in a body, and the BFF forwards the real client IP explicitly because APIService stamps X-Forwarded-For from its own inbound request, which would otherwise put every viewer in one rate-limit bucket keyed to the pod IP. Adds SECURITYSERVICE_URL to the configmap (verified: kamowssecurity-service exists in the kamo namespace).

  2. · SecurityService

    Quote REST surface — staff management and public share links

    Staff endpoints at /api/security/commerce/quotes and an unauthenticated resolver at /api/security/public/quotes. QuoteController extends BaseLosController rather than copying POSController's preamble: CallerSessionResolver accepts the X-OTK attribute, the X-***-Token header AND the *** cookie, where POSController's private getOrgId reads only the OTK attribute. Every handler is rights-gated (VIEW/MANAGE/SEND_QUOTES). Most of the commerce surface is session-only — any authenticated member of any org can mutate orders — and that is deliberately not repeated here, because a quote commits the org to a price in front of a customer. QuotePublicController needs no security-config carve-out: the Spring chain is anyRequest().permitAll() and OTKPreAuthFilter only acts on an X-OTK header, which a browser following an emailed link never sends. Design points: - The token arrives in the POST body, never a URL. APIApp.forward() logs request lines, and a bearer credential in a path would land in those logs. - Not an enumeration oracle: unknown, revoked and org-mismatched tokens all return one indistinguishable generic failure. Only EXPIRED gets a specific, honest answer, which is safe because the caller already proved possession of a 256-bit token. - The org derived from the request host is cross-checked against the org on the link row. Without it, a valid link for org A opened under org B's host would render A's pricing wearing B's branding — a phishing primitive. - no-store / Referrer-Policy: no-referrer / X-Robots-Tag: noindex on every response. The page loads logos from theme.<org>, and a Referer would carry the token off-origin. PublicLinkRateLimiter is Redis-backed because SecurityService is multi-replica — in-process counters would be a limiter in name only. It is explicitly DoS and log-hygiene protection, NOT the anti-guessing control: 256 bits of entropy is. Failures are counted far more harshly than successes, since one office opening a forwarded quote fifty times is normal while fifty misses is not. It fails open, so a Redis outage cannot take the quote page down. QuoteEmailClient reuses the EmailService transactional path (X-Internal-Auth, QUOTE_SENT template) and sends only the headline figure plus the link — never the itemised pricing, so a forwarded email is not itself the disclosure. Pre-existing test failures unchanged by this commit (verified by stashing this work and re-running on clean HEAD): **************** NPE on a null EntityManager, and the legacy com.retval SecurityServiceApplicationTests missing a @SpringBootConfiguration. REQUIRES a KamoInitializerService run before these endpoints work: the quote tables are new and ddl-auto is none here, so the service boots normally and only the quote endpoints 500 on a missing relation until KI has run.

  3. · kamo-shared-library

    Quote/estimate domain — entities, pricing engine, share links

    Adds a first-class Quote alongside the existing commerce spine. The pre-existing "ServiceQuote" is an Intent plus a JSON blob carrying one offering and one flat amount, with no recipient, no expiry and no UI; it is not a foundation. Extending Intent directly was rejected too: commerce_intents is shared by LOAN_APPLICATION, RENTAL_REQUEST, SUBSCRIPTION_SIGNUP and PURCHASE_REQUEST, so ~25 quote columns would put permanent NULLs on the mortgage and rental paths. Entities **************** Quote engagement-linked, versioned, valid_until + valid_for_days QuoteLineItem snapshots name/sku/price so a sent quote never re-prices QuoteShareLink bearer credential, SHA-256 at rest, revoke/expiry/view counts QuoteLinkView per-open audit (ip, user-agent) QuoteEvent append-only lifecycle trail Totals are server-authoritative. QuoteTotals is the single writer of every money field and the request DTOs carry no totals at all — deliberately unlike createRetailOrder, which takes subtotal/tax/total verbatim and can be handed a populated order with total = 0. Line discounts apply before the quote-level discount, discounts can never exceed their base, intermediate maths stays at scale 4 and only the grand total rounds to the currency's minor unit. Share tokens: 32 bytes of SecureRandom as 43 base64url chars (256 bits). Same entropy as the e-sign signer token but 21 chars shorter, keeping the emailed URL under the 76-char plain-text wrap limit. Only the SHA-256 is persisted. Tax is honest rather than invented: the platform has no tax engine (TaxClass/ TaxZone/TaxRate are authorable and read by no calculation), so ENGINE mode reports NOT_CONFIGURED instead of fabricating a figure on a commercial document. QuoteLineItem.taxClassId is UUID, matching TaxClass's real PK — Offering.taxClassId is a Long against a UUID-keyed table and is structurally unresolvable. Also: rights 178-180 (VIEW/MANAGE/SEND_QUOTES) and the QUOTE_SENT canonical email template, registered in CANONICAL_KEYS (absent there means a permanent 404). 35 tests covering the pricing engine and token hygiene. Full suite: 896 passing. Schema note: these are new tables. KamoInitializerService must run before SecurityService serves the quote endpoints — ddl-auto is none there, so the service boots fine and the endpoints 500 on a missing relation until it does.

  4. · KlusterServices

    CiliumNetworkPolicy として、アバターリゾルバーのエグレッションが含まれています

    8f6c672は、実際にここで動作する唯一のフォームで、反転したものを再インスタンス化します。 単なるNetworkPolicyがこれを表現できないため、以前の試みが失敗しました クラスターのトポロジー。 SecurityServiceの4つの重要な依存関係が3つあります。 ノードは、Podネットワークではなく、 cockroachdb-0 と nats-k1m1-0 は hostNetwork を実行します。 そして、Minoはホストプロセスなので、podSelectorはそれらを一致させません。 ザ・オブ・ザ・ 悪意のある部分、最初のパッチの試みも失敗した理由は、 ノードサブネットの ipBlock はそれを修正しません。Cilium はノードを以下のように分類します。 予約されたホスト/リモートノードのアイデンティティ、および CIDR のルールは非適格に一致しません 特定非営利活動法人 toEntitiesは、それを表現する唯一の構造です。 どちらの失敗モードも、書き込む前に捨てられたカナリアポッドに対して再現された これは、生産に対してではなく、 カナリアは、以下の同じ規則を行わ ipBlockバージョンがサイレントブロックされた方法の異なるラベル CockroachDB、NATS と MinIO を完全に適度に探しています。 カナリアで検証し、再起動なしでSecurityServiceで確認し、 接続エラー: ALLOW dns, crdb:26257, nats:4222, minio:9000, redis:6379, 兄弟:80, 世界:443 ブロックノード:22、ノード:443、kube-api:443、redis-via-node:6379、169.254.169.254、ループバック:9000 最後の 4 つは、解決者の SSRF がどこにも行かない点です。 ザ・オブ・ザ・ インコードガードはプライマリコントロールを維持します。それは理解する唯一のレイヤーです DNSのリバインド - これはそれの背後にある防衛です。 ヘッダーはカナリアのプロシージャを運びます。 これを変更する人は、それを再実行する必要があります その理由よりも、Ciliumのアイデンティティモデルは、その方法が明白ではない NetworkPolicyの直感は提案し、ステージング環境はありません.

  5. · kamo-internal

    グループの会話のためのfacepileを表示

    その上で5人の人を持つ糸は、1つの任意の顔で表されていました。 1人からメッセージとして読みます。 参加定員が3名まで。 名線を解析することなく、一目で「重ねる人」が目立っています。 3 でおおわれた: 円は回復力および列の下で縮まることを過去に 情報ではなくテクスチャになり、残りは既に記載されています。 右側にある「その他」のテキスト。 読者は自分のスレッドリストからフィルタリングされ、他のパーティーリードです。 左から右へ読み込まれるので、第一次対応は一番左端に 未踏のバッジを運ぶもの。 各面は列のリングを取得します 背景色; 同様の写真のスタックが1つの形状にマージすることなく。 決議は、リードだけでなく、最初の少数の参加者をカバーしています。 それ以外の場合は、第2面と第3面は、永久にモノグラムとして座ります.

  6. · SecurityService

    チェーンを計測し、期限切れのエントリを返し、開示を記録

    この機能の故障モードはすべてサイレントです。 定形遮断器、作業員 キューは、すべてのタスクを破棄し、HTMLを返し始めたプロバイダ - それぞれが見える まさに「この特派員は写真がない」と似ています。 挙動は似ています。 作業との間のユーザー可視差はありません。 完全に死んだので、測定する必要があります。 メトリック(Prometheus は既に配線されていたので、これは主に命名): キャッシュヒット 比率、鎖の持続期間は答えた層によって、per-providerの衝突/従いました、 ワーカーのキュー深さ、および捨てられたタスク合計。 以前使用したのは 設計でサイレントに落ちるDiscoverPolicy。現在はカウンターを増やし、 フィールド初期化が実行されるため、実行前にカウンターが宣言されます ソースの注文と怠惰なキャプチャに依存することは、無害な再オーダーを破棄します。 ディスクロージャーは自分のロガーに行きます。 本人のみが個人である 開示 — ドメイン層は、アドレスの右側を送信します。 何も識別しない — ではなく、呼び出しの前に記録されるので、 リクエストが失敗してもトレイルが存在します。 アドレスは全部で書かれています。 deliberately: 「誰がこの人のデータが共有されたのか」と答えることです。 回答できないハッシュ。 リーパーはリークを閉じます:deleteExpiredBeforeが存在し、何も呼ばれません。 sender identity は永遠に成長しました。 多忙なメールボックスは、何千人もの対応者に触れる 毎回ネガティブな行を残しておきましょう。 興味深いケースはあるので、テストは実質のソケットに対するフェッチャーをカバーします、 ロジックではなく、すべてのプロバイダの動作: 404 有効なイメージを運ぶ (DuckDuckGo), ソフト 404 サービング HTML, 原点アンダースタッキング コンテンツの長さ, a 1x1 のスタブと、ポッドネットワークに向けられたリダイレクト。 生産のクライアントは、 ループバックテストサーバーでポイントされる — DNS ピンは何かを解決することを拒否します 全体的にはプライベートなので、リラックスしたクライアントや ピンは別々に主張されます.

  7. · kamo-internal

    ターゲットを絞ったLLPA行列をコンソールから調整する

    インジェクションパネルは、レートシートのみを公開できます。 LLPA MATRIX ソース用 「LLPAの調整として推進する」カードが表示されます。 LLPAのレートシート版は(+任意グループ)調節し、それぞれは承認しました FICO/LTVのバケツは条件付き価格の調節になります。 結果名 結果名 多くは作者され、正直なところ、バケットから何行がスキップされたか 安全に読み取れなかったり、既に存在していたりすることができません。 ingestionApi.promoteの調節 新しいゲートウェイリレーに投稿します.

  8. · SecurityService

    ゲートウェイリレーは、LLPA マトリックスを条件調整に促進

    ******************** 増加促進調整, ミラーリング 昇格シートのリレー: 神有なゲート、組織Id サーバにスタンピング write() を経由してペイロード、ダウンストリーム 4xx は動詞を中継しました。 スチュワードを回転させる LLPA の行列をコンソールから実際の per-scenario 価格調整に見直しました.

  9. · VOIPService

    録音、ボイスメール、トランスクリプトアクセス

    録画再生は痕跡を残さない。 記録された呼び出しは、 densest PHI です プラットフォームストア — 未処理の会話 — と "このことを聞いていた call" は unanswerable で、最初の質問は §164.402 違反 評価依頼 Wires CallRecordingAuditor をすべての読み込みに公開します。 - GET /recordings/{id}/audio CALL RECORDING/ダウンロード メニュー CALL RECORDING / ダウンロード - GET /recordings/{id} CALL RECORDING + TRANSCRIPT / VIEW メニュー CALL RECORDING / ダウンロード - GET /instances/{id}/voicemails TRANSCRIPT / リスト、行 トランスクリプトは隠すものです。 メディアのダウンロードではありません。 メタデータ JSON と各ボイスメール行の内部で、同じ PHIは、チップの形でオーディオとして。 自分だけのイベントだから 開示の経理は、リソースごとに組み立てられます。 PhiAuditAvailabilityAdviceがログできない状態に陥らないので、 500 ではなく 503 に読み込まれる — リクエストは細かいものだったので、デコードして PHI のアカウントは提供できません。 拒絶反応も記録されます。 どんな検出が見えます。 また、配線中に見つかった承認ギャップを閉じます: POST /recordings/lookup はリクエストボディからインスタンスId を取り、決して取りませんでした caller の org が所有しているので、別のテナントのプロバイダーを解決しました。 id を録音 ID に呼び出します。 他のすべてのインスタンススコープエンドポイント サービスはすでにfindByIdAndOrgId を経由して行きます。このサービスはもうありません。 再生許可の記録は、as-is を残し、所定の場所に文書化されます: org 所有権は唯一のチェックであるので、どのメンバーでも記録を再生することができます 彼らの組織。 "own Extension" が「own Extension」かどうかを指す バウンダリと、その権利はそれを持ち上げる - それは製品決定ではなく、 ここで作ろうとすると、すべてのアクセスが少なくとも記録されます.

  10. · AIService

    保存されたAIの会話履歴を全て読み込む PHI アクセスを記録

    共有された §164.312(b) が永続したプロンプトを読み込むパスに回転させるワイヤー そして完了。 アシスタントはモジュールです コンプライアンス評価 置く 境界線を外す — BAA および BAA なしの外部プロバイダにファンを要求します その後、無期限に保管されます。そのため、そのストアの読み込みは最も多く開示されます トレイルの必要はなく、 オーディション:セッションリストと履歴検索(会話ごとの1つのイベント、ではなく) 単一の会話のメタデータとそのメッセージ、パブリック 自身のトランスクリプトをバックに、ハンドオフバンドルを読み込むウィジェット。 参考文献 あまりにも録画 — 別の org の guids をプロービングすることは、検出が何であるかです。 unauthenticated 試みは下がるのではなく sendinel org に起因します、 レコーダーは、それが属性にできないものを拒否し、それが最もイベントであるので、 価値ある維持。 handoff バンドルは VIEW ではなく DISCLOSE です: 訪問者の AIServiceの会話と、他のプロダクトの人間の前に。 お問い合わせ 親切は閉じられていないので、失われたレコードは今、トランスクリプトと答えを保ちます 503 ではなく、データを扱うのではなく、 識別子のみ。 メッセージ本文、タイトル、または検索語は絶対にありません。 自分のアシスタント履歴を検索すると、借り手の名前が入力され、 プロンプトを再現した監査証跡は、PHIをPHIに移行しただけです。 ログストリーム。 d428ee3 の同時セッションが途中でスワイプする: オーディション者 レコードが不足していたDeniedInternalViewとPublicAiChatControllerの新機能 constructor 引数はテストに達しなかった.

  11. · MediaService

    Record chat message reads on the endpoints that return bodies

    Sending a message left a row; reading one left nothing. Every chat, support and social thread on the platform renders from a handful of endpoints here, and none of them recorded anything, so "who read this conversation" had no answer — which is the first question a 164.402 breach assessment asks. ChatMessageAccessAuditor emits a PhiAccessEvent (CHAT_MESSAGE) from the five paths that hand a caller message text: GET **************** LIST GET /api/media/sessions/chat LIST POST **************** VIEW GET **************** LIST POST **************** VIEW One event per message, not one per request. "They opened the thread" is not an answer to "was this individual's message disclosed", and the message ids are already in hand at every call site, so a request-shaped row would have been a choice rather than a limitation. The thread read records AFTER the mute filter, so a row means a body that caller was actually handed. The chat list is in scope because each row carries up to 140 characters of the thread's last message — that is content, not metadata. Rows with an empty preview record nothing. Deliberately NOT recorded: unread counts, presence, last-seen, typing, read receipts, session-member lists, mutes, ticket detail (metadata plus status history, no bodies) and support reporting aggregates. A trail is only useful if a row means someone saw content; rows that stand for nothing bury the ones that do. Refusals are recorded too, against the thread guid — no message id exists yet at the point of refusal, and repeated refusals against thread guids is the shape probing has. An unauthenticated reader is attributed to a sentinel org rather than dropped: the recorder rejects unattributable events, and that is the single most interesting event this trail can hold. Client IP comes from X-Forwarded-For with remoteAddr as fallback — everything arrives via Traefik, so remoteAddr alone records the ingress pod. Public chat takes a second hop through APIService, which sets X-Forwarded-For to the visitor address and moves the browser's User-Agent to X-Original-User-Agent, so the auditor prefers that header; the literal User-Agent on that hop is APIService's own HTTP client and identifies nobody. Visitor reads are attributed to the org from X-Public-Chat-Org-Id with no member id: a visitor has no member identity of their own, but the org owns the records, so the read stays attributable. Identifiers only, never message text. An audit trail that reproduces the message it audits has just copied the PHI into the log stream. Also completes eed3817, which swept this work in mid-edit and left MediaController referencing PhiAccessKind before the import existed — that revision does not compile. This one does: mvn -o test is 16/16 green.

  12. · DocsService

    何も記録されていない4つの文書パスでバイトアクセスを記録

    /download は、************************ を経由して、 プラットフォームのみバイトアクセスインジェクションサイト。 兄弟のエンドポイントは、 同じバイト — /stream, /bulk-download, テンプレート変換-PDF と テンプレートページサムネイル — 何も記録されていないので、誰でもドキュメントバイトを望む トレースを離れることなく、異なるURLを選択する必要がありました。 現在、PhiAccessEvent をポイントアクセスで確認し、 bytesは顧客のために提供いたします。 一括ダウンロードは、1つのリクエストではなく、1つのドキュメントにつき1つのイベントを生成します。 ツイート 400文書の書き出しは「この個々の記録を巻き込む」という答えではありません 開示、および文書の行は、6年後の回答可能なものである。 §164.528の下。 クライアントIPは、X-Forwarded-ForからフォールバックとしてremoteAddrが付属しています:すべてのリクエスト Traefik を経由して到着するので、remoteAddr 単独でイングレッションのポッドを記録する すべてのアクセスは、検出のために価値がないです。 不正な試みは、よりではなく、送信された組織に帰属します。 ダウン — レコーダーは、不当なイベントを拒否し、文書のリクエスト セッション無しのバイトは、保持する価値のあるイベントです。 これは、それらを置き換えるのではなく、既存の ImgLogDownload 行と一緒に座っています。 これらは、画像ドメインの記録であり、これはクロス・サーフェス・トレイルの会計です 開示は随時受け付けております。 メモ ImgLogService、これらの行を読み込み、まだ プラットフォーム内でゼロの発信者を持っています.

  13. · KlusterServices

    アバターのリゾルバーからエグレッシブが含まれています。 Redisのエヴィクションプールに注意

    SecurityService は、ターゲット URL が信頼できないフェッチャーを実行します。 パーティー — BIMIレコードは、送信ドメインを所有している人によって公開されたDNSコンテンツです。 テナントにメールを送ることで、誰でも送信ドメインになります。 このクラスターの何もない そのような要求が進むことができる場所の禁忌:Redisは6379年に未確認に答えます、 MinIO on 9000 で root 認証, CockroachDB に 26257, ノードメタデータ どのPodからもエンドポイントが到達可能です。 ソルバーは、コード内で独自の制御を強制し、これらはプライマリを維持します。 DNS のリバインドを理解する唯一のレイヤーであるため、防衛。 お問い合わせ 2つ目は、まだプライベートアドレスに到達できないバグです。 allow-list ではなく、意図的に許可します。 これらのサービスは合法的にリーチします ホスト, そのため、目的地を列挙することはできず、壊れるだろう 静かに。 代わりに、公共のインターネットをすべてのプライベートレンジで許可します。 その後、実際に必要な特定のインクルーザーポートを再許可します。 ポッドにスキャッピング 信じられないほどのフェッチを実行する — それを名前空間全体に適用するとオフ 決して監査されていないサービス。 Redis の変更は、行動の変化ではなく、コメントとデータベースのバグです。 それは、 sender-avatar キャッシュが短いメタデータ文字列だけを保存する理由: maxmemory 2gb は、インスタンス全体に allkeys-lru の evicts を持つ。DB 0 は、すべてのライブを保持します。 セッションでは、バルクバイトのキャッシュがここにユーザに圧力の下でランダムに署名します 日々の診断を怠る.

  14. · kamo-internal

    送信者と受信写真を表示

    Mailbox は、すべての人のための単一の共有勾配に単一の文字をレンダリングしました。 すべての行は同じと円が情報を保持していませんでした。 今、ショー ひとつが知られるときの実写写真と2文字のモノグラム です。 1つのSenderAvatarは7つの手巻きの初期の円を交換します。 お問い合わせ 一貫性以上のもの: それぞれがアドレスを分割する方法の独自の考えを持ち、 アバターのキャッシュは、その分割にキーが付けられているので、リストとの間の任意の不一致 読書ペインは静かにヒット率を半分にし、同じ人物を2つ示します 1つのスクリーンの異なった方法。 parseAddress は RFC 5322 の唯一の場所 アドレスを離れます。 リストとスレッドビューは、生ヘッダーの印刷を停止します "Jane Doe <jane@acme.com>" のように、名前を表示します。 アイデンティティは、メッセージオブジェクトに決して、アドレスによってキーを付けられた独自のストアに住んでいます。 リストビューの両方が30秒ごとにポールし、毎回Websocket通知で表示されます。 メッセージの配列の卸売を置き換える — メッセージにマージされるものは 毎分2回拭いたので、アイドルの受信トレイはモノグラムとバックに写真をフリッカー 永遠に。 アドレスによるキー化は、後続回答が塗装できないことを意味します 行がキーにされていないので、間違った行。 リクエストは石炭処理 スクリーンごとの1つのバッチに:独立して尋ねるfty列はftyです リクエストは、このサービスへのすべての呼び出しが1回限りのキーを差し込みます。 実は百人。 サーバ側から45秒後には、見逃しが再発されます。 解像度は2相で、不明なアドレスは後者の呼び出しでのみ回答されます。 インタビュー ListViewが仮想化されました。 ページを50行目し、再レンダリング すべての投票でパーインデックスのストガーを持つ AnimatePresence 内にあるそれらすべて; それぞれにイメージを加えると、メールボックス内の最も重い表面になります。 返信なしとメーラーデーモンの送信者は、モノグラムではなくニュートラルマークを取得します。 バウンス通知以外にも積極的に誤解を招きます。 写真は装飾です (alt="") 名前はそれら横に常に目に見えるテキストであり、運ぶので 参照rerpolicy="no-referrer" なので、スレッド識別子はイメージホストに到達しません。 すべてのアバターは、独自の起源から提供されます。サードパーティの画像が取得されます。 検証済みで再ホストされたサーバー側 — これは、新しい per-request を img-src ポリシーはテナントのドメインにピン留めされます。 その方針は、 次ではなくプロキシ。 このプラットフォームがホワイトラベル付きであるため、設定: 静的な方針は1つのテナントの名前をつけ、残りの部分のためのイメージを壊す必要があります。 また、連絡先から既存の保存されたトラッキングピクセルベクトルを閉じます。 写真Url 複数の <img src> で直接レンダリングされた無料のテキスト URL です。 リモートSVGは、次の/画像は使用できません。 それだけで許容リスクだった SVGsは、独自のテーマロゴでした。プラットフォームのインジェストがマークする今ではありません サードパーティのDNSで公開 コンタクトコーダは、ハードコードされたORG ID = 1とorgIdクエリパラメータをドロップします。 セッションから派手なテンシーを導き出すバックエンドの変更にマッチします.

  15. · EmailService

    Avatar endpoints; fix unauthenticated reachability and contact tenancy

    Adds the mailbox side of sender avatars, and fixes three pre-existing holes this work would otherwise have built on top of. AUTHENTICATION. OTKPreAuthFilter used to reject a *bad* OTK but treat *no* OTK as anonymous-and-allowed, falling straight through to the filter chain — and the Spring Security chain here is anyRequest().permitAll(). Every mailbox, contact and calendar endpoint was therefore readable by anything that could reach the ClusterIP. Protected prefixes now fail closed. The fix lives in the filter rather than in the security chain deliberately: reshaping the chain would touch thirty unrelated surfaces at once, including two servlets and the DAV endpoints. The exempt list is the complete set of genuinely session-less entry points — the CAN-SPAM opt-out link, the OAuth redirect target where the tenant rides in the signed state parameter, and the server-to-server surfaces that authenticate with X-Internal-Auth. CONTACT TENANCY. Every contacts endpoint took orgId as a query parameter, or reached a book by id with no ownership check at all, so changing a number in the URL read another tenant's contacts — names, addresses, phone numbers, notes. The org now comes from the session and every book- or group-scoped operation proves the target belongs to it. Absent targets return 404 rather than 403 so an id in another tenant is not confirmed to exist. orgId is still accepted on the two list endpoints and ignored, so clients do not break mid-deploy. That change also fixes a silent bug: searchContacts derived orgId from bookId and passed null when no book was selected, into a query whose first predicate is contactBook.organization.id = :orgId. Book-less search therefore always returned nothing — which is exactly how the mail recipient autocomplete calls it. EMAILLOGOSERVICE. It fetched an operator-supplied URL with no scheme or address check, and transcoded the result on a Batik that resolves external references by default. A logo SVG containing <image **************** would have been dereferenced from inside the cluster. Now guarded on both ends. MESSAGEINDEXER. resolveMemberIdFromEmail pulled the entire members table into heap and filtered it in Java, once per indexed message, on a 1Gi pod — the code said so in a TODO. Same semantics, one indexed query. The avatar surface is one batched endpoint rather than a field on the message DTOs. Both list views poll every 30s and replace their message array wholesale, so an avatar carried on a message would be wiped twice a minute and the UI would flicker forever; and EmailMessage is shared by the folder list, reading pane, thread view and search, so changing its shape would touch all of them at once. The directory-photo endpoint is server-to-server only — exposed to a browser it would be a directory-enumeration oracle for the tenant.

  16. · SecurityService

    Sender-avatar resolution chain for the mailbox

    Resolves a correspondent's email address to a picture, working outward from what Kamo already knows: member photo, then a photo someone attached to a contact, then the tenant's connected Microsoft/Google directory, then the sender domain's BIMI mark, then its favicon, then Gravatar. Anything unresolved renders a monogram, so the chain always terminates locally. The directory tier is worth calling out: the connect flows here already request admin.directory.user (Google) and User.ReadWrite.All + Directory.Read.All (Microsoft) alongside the mail scopes, so for Workspace and 365 tenants this returns authoritative photos with no re-consent and no third party involved. It should beat anything on the public internet and usually will. Two phases, never one. A request reads cache only; unknowns go to a bounded worker with DiscardPolicy — running an overflowed task on the caller would hand a request thread to a third-party host, and a discarded resolve costs nothing because the next poll asks again. A full-miss chain is DNS plus four HTTP calls, and a page carries dozens of addresses; nothing about opening a folder may depend on third-party availability. Decoupling also keeps resolution from correlating with a human opening a message, which would make it a read receipt — the exact behaviour the mailbox already blocks by default for remote images. This is the platform's first fetcher whose target URL is chosen by an untrusted party: a BIMI record is DNS content published by whoever owns a sending domain, and anyone becomes a sending domain by emailing a tenant. There is no egress NetworkPolicy and no forward proxy, so the controls are in code. SafeImageFetcher pins DNS — a custom resolver hands OkHttp only the addresses that passed PublicHostGuard, closing the rebinding window that defeats a naive pre-flight check — refuses redirects, caps bytes as they stream rather than trusting Content-Length, and requires an image content-type. SvgSanitizer parses and re-serializes against an allowlist instead of rasterizing. Batik 1.17 resolves external resources by default, so handing it a hostile SVG would dereference <image **************** from inside the cluster after the URL check already passed. The Tiny PS profile forbidding that is a statement about conforming files, not about bytes fetched from an attacker's URL. Provider miss semantics are all different and all cache-poisoning if treated as hits: Gravatar is asked with d=404 because every other default returns 200 with a placeholder; Google's no-match is a 404 carrying a byte-identical globe, so it is rejected by content hash; DuckDuckGo returns 404 with a real image body, so status decides. Bytes are re-hosted under a random name — never one derived from the address, which would let anyone probe whether a given person corresponds with this platform — and served through an authenticated route. Redis holds metadata only. That instance is 2GB with allkeys-lru on an emptyDir, sharing a keyspace with every live session; caching image bytes there would evict sessions and sign users out at random.

  17. · kamo-shared-library

    Sender identity model + org-scoped batch email lookups

    Backing store and queries for the mailbox sender-avatar chain. SenderIdentityEntity is the durable tier of the avatar cache. org_id is part of the natural key rather than decoration: USERS.EMAIL is unique only per security provider, so an answer derived from tenant data must not be visible to another tenant. External-tier answers (BIMI/favicon/Gravatar) are genuinely global and share a sentinel org so every tenant shares the hit rate. Misses are stored as rows, not absences — without that, every unresolvable sender re-runs the whole provider chain on every inbox poll. OrgAvatarSettingsEntity splits the chain by what actually leaves the building. The identity tier defaults OFF because it discloses a digest of a correspondent's full address to a third party, which on a lending CRM is a decision about a borrower identifier rather than a display preference. The repository additions exist because none of this was reachable before: - ContactEmailRepository had no lookup by address at all — only substring LIKE for the autocomplete — so a contact that had a photo could not be resolved from a From: header. - Member/User batch lookups are org-scoped and take pre-lowercased input. LOWER() is applied to the stored column only; emails are persisted verbatim (EmbEmail has no normalizer) and CockroachDB equality is case-sensitive. - The avatar-id projections return ids rather than entities on purpose. Both Member.avatar and User.avatar are lazy associations onto a JOINED hierarchy: a caller outside a transaction gets LazyInitializationException, and one inside gets a base-type proxy that never narrows to AvatarPhoto, putting the file extension needed to build the URL out of reach. - **************** replaces a findAll() full-table scan that ran once per indexed message. PublicHostGuard is shared rather than duplicated because it is a security control: it decides whether an address is somewhere we will send a request that an untrusted party chose. Tested against the real bypass techniques — IPv4-mapped IPv6, 6to4, NAT64, CGNAT and link-local metadata. Right 177 gates the disclosure decision.

  18. · kamo-shared-library

    Audit spine for PHI access — §164.312(b)

    The Security Rule requires recording access to ePHI, which means reads. The platform records changes well — LeadHistory, SystemAccessLog, UserHistory, LoanAuditEvent — and reads almost nowhere, which is why "who viewed this record" is currently unanswerable. That is also the first question a §164.402 breach assessment asks, so this one gap blocks both the audit control and the four-factor analysis. Capture layer only; no schema. The durable store sits behind PhiAccessLogWriter so call sites can be wired now and the sink can evolve — plain table, then append-only with a hash chain, then a signed export to object-locked storage — without rippling back through hundreds of call sites. Two properties carry the weight: - Failure handling belongs to the access kind, not the caller. EXPORT, DOWNLOAD and DISCLOSE fail closed: an unlogged bulk export is exactly the event you cannot reconstruct afterwards, and "we served the data but lost the record" is the worst answer to give an investigator. VIEW, LIST and SEARCH fail open and log loudly, because losing one line is not worth taking the product down. That asymmetry is deliberate and wants signing off rather than assuming. Callers under pressure resolve policy questions in whichever direction unblocks them, so the policy is not at the call site. - A denied read is still a read. Refused attempts are recorded, because the access pattern of someone probing records they cannot reach is what detection needs and is invisible if only successes are logged. Startup safety, which nearly went wrong: PhiAccessRecorder is a @Component and every service component-scans com.kamo, so a hard dependency on a writer bean would have failed context startup across ~40 services at once — a whole-platform outage from a feature nothing had called. The first fix, a @Configuration with @ConditionalOnMissingBean, does not work: component-scanned configuration is processed alongside user configuration rather than after it, so both writers register and injection fails on ambiguity. PhiAuditContextWiringTest caught that. The fallback now lives in the recorder via ObjectProvider.getIfUnique, where no ordering question exists, and ambiguous writers degrade to the default rather than failing startup. The default Slf4j writer is a starting point, not the destination — log lines are not tamper-evident and retention is 14 days against a six-year obligation. It exists so call-site wiring does not wait on the store. Identifiers are logged, content never is: an audit trail that reproduces the record it audits has just moved the PHI into the log stream.

  19. · SecurityService

    コンプライアンスの境界が拒否されたときの403ではなく500

    FeatureService.enableFeatureの2つのコールサイトがHIPAAの境界を理解しています。 - /enableマップPhiModuleNotPermittedExceptionに403の理由を運ぶ、 AppliedModelViolationException の処理を直接上回る。 なし これは、以下の広範なキャッチは、500に非審美的なポリシーの拒否を有効にしました。 - isPhiPermitted でリストパスの前チェックのオートエンタブルループ ツイート 完了したServiceTypeを全て実行するだけ*listing* 組織の特徴は、最初のブロックされたモジュールで投げるので、 1つのエントリを隠す代わりに、PHIテナントのためにアンロード可能な機能ページ。 現在、すべての組織が handlesPhi=false です.

  20. · kamo-shared-library

    機能スイッチでコンプライアンス境界を強化

    ワイヤー PhiTenantGuard を FeatureService に、単一のチョーク ポイントを通す モジュールは組織のために転換されます。 境界線が止まるところ ポリシーオブジェクトは、執行を開始します。 両方の半分は、一人でギャップを残しているので、 - enableFeature は PhiModuleNotPermittedException をスローするので、PHI テナントは PhiModuleNotPermittedException をスローする モジュールのカモのスイッチは、ビジネス・アソシエイトとして遅れることができません。 - isFeatureEnabled(組織、ServiceType) は、いつでもモジュールの無効化を報告します。 テナントはPHIを扱い、モジュールは境界の外にあります、何でも OrgFeature 行は言う。 行は正当にアクティブと言えるでしょう。 テナントが PHI テナントになる前に、 enableFeature は再び実行しません その移行。 isPhiPermittedは、反復する発信者のための非閲覧形式です。 機能制御回路 ※listing* で完了した全ての機能が自動使用可能なので、最初に投げる ブロックされたモジュールは、1つのエントリをスキップするのではなく、すべての機能ページが失敗します。 PhiServiceTypeMappingはPhiModuleにServiceTypeを結合し、テストと合計です どのマップされていない値でもビルドが失敗します。 そのテストは、最初にその継続を獲得しました run: enum の grep が見逃していた ServiceType.STORAGE をキャッチし、 AI CHAT、DOC MANAGER、DOCS EDITOR、DOC CLASSIFIER。 それなしでは5つのサービス 型は、境界線の外にサイレントにされている - 同じ省略障害 PhiModuleのコンストラクタが閉じ、レイヤーを1つ再導入しました。 以前にマッピングされていないサービスタイプの新しいモジュール、それぞれに明示的 処分:オブジェクトストレージ、ドキュメントエディタ、分類器、サポートチケット、人事、 在庫、法的、計算機および活動の供給は中です;クラブハウスおよびゲームはあります BLOCKED PENDING WORK は評価されていない地面で、 想定するよりもブロックする理由 すべての組織は、今日のPhi=falseを扱うので、この変更は動作しません 生産。 最初のテナントがフラグを立てる瞬間にロードベアリングになります.

  21. · kamo-shared-library

    アプリケーション作成時に MERS 互換融資番号をオートアサイン

    LoanFile.loanNumber は作成に null だった (空白作成とリードプロモーション) スティワードが 1 つをタイプしたときだけセット — ブロックされた MERS MIN minting(最小10桁のセグメントは融資番号です)とすべての下流 頭のないローンの識別子。 AppMortgageService は、両方の作成サイトでローン番号を割り当てます。 10桁の数値(MinMinter は非数字を除去し、 <=10-digit セグメントを必要とします。 そのため、純粋な数値は、唯一の安全な形状です)。 (org id,ローン number) のインデックスをバインドしたリトライでインデックスします。 FAILS は、null にオープン 衝突の縞か利用できなくなったレポ — 前のマニュアルに劣化させる アプリケーション作成を破るよりもむしろ行動する。 貸し手支援 ローンファイルパッチパスを経由した番号はまだオーバーライドします。 スキーマの変更はありません( 既に存在するカラム、インデックス化).

  22. · kamo-signer-monorepo

    受信者の決議されたタイムゾーンのレンダーサイダーの日付

    SigningSession は、ESigService が解決する必須の `timeZone` (IANA id) を獲得します。 受信者のメンバーゾーンから、その後、 org's、 UTC。 ウェルカムスクリーン フォーマット者に明示的に渡すので、有効期限はどちらも依存します ビューアのデバイス設定やプロバイダのUTCデフォルト。 フィールドを作る オプションではなく、1つのリテラルセッション建設現場をキャッチ コンパイル時に (preview.ts) をコンパイルします。 また: - locale レイアウトで setRequestLocale を呼び出すと、URL にバインドされる翻訳 ミドルウェアヘッダーだけに依存するのではなく、セグメント - メッセージ/ が `turbo prune` を存続し、文書を継承した Dockerfile でアサート .dockerignore がビルドコンテキストから .gitignore を保ち続ける必要がある理由 カップリングプルーンは、翻訳と画像の船のレンダリングの生鍵をドロップします 署名者が文書を開くまで失敗しない

  23. · ESigService

    会員から署名者表示タイムゾーンを解決してから、org

    署名の日付は、クライアントがデフォルトで任意のゾーンでレンダリングされたため、 envelope expiring 2026-08-03T05:00Z は "Aug 3" を パシフィック サイダーに読むことができます リンクがすでに8月2日の夕方に死んだ人のために SigningSession は、サーバーが解決する IANA 時間Zone を実行します。 会員である場合、独自の Member.timezone を設定し、送信する 組織・タイムゾーン、その他のUTC。 SignerZone はテストで純粋なソルバーです。 独自のゾーンにすべてのタイムゾーンTypeマップをアサートする1つを含む新しい enum はクライアント側の RangeError を出荷できません。 また、enum名(SPANISH)を発したSigningSession.localeを修正しました。 署名 URL の locale セグメントで使用される BCP-47 コード ("es") ではなく.

  24. · InitializerService

    HANDLES PHIとPHI EFFECTIVE DATEをorgsで作成

    OrgBrandingFieldsMigrationに従う2つの新しい組織分野を、映します コンベンションなので、コラムはただだけではなく、どんな環境でも再現可能 ddl-auto パスが実行する時に存在する。 HANDLES PHIはNULL DEFAULT FALSEではないので、既存のテナントは外部に明示的に コンプライアンスの境界。 PHI EFFECTIVE DATE は TIMESTAMPTZ ではなく TIMESTAMP です。 実体フィールドは LocalDateTime です。 テストはドリフトガードで、休息ではなく、列名をオフに読み取ります。 リフレクションによるエンティティティティは、エンティティティティフィールドがALTERを持たないときにビルドを失敗します。 お問い合わせ この慣行の実際の失敗モードです。誰かがフィールドを追加し、忘れます migration と ddl-auto:none をサービスで製造する ビルド時ではなくクエリエラー。 変異によって検証:除去 PHI 効果的な 日付 ALTER はアクション可能なメッセージに失敗し、それを回復させます 緑に行きます。 両方の列は、クラスターに既に存在しています。これらのALTERは重要ではありません.

  25. · kamo-shared-library

    パーシストは組織のPhiとphiEffectiveDateを扱います

    バックス PhiTenantGuard 実際の列で. HANDLES PHIはNULL DEFAULTの偽物ではないです 続いて**************** パターンなので、すべての既存のテナント 黙って内部ではなく、コンプライアンスの境界外に明示的に存在します。 PHI EFFECTIVE DATEは、侵害評価と保持義務を負います。 右側のウィンドウ — 「いつから」 は、最初に「露出されたもの」と尋ねた質問です。 ********************************でポリシーに変換する ガード それ自体は、エンティティティティの自由を維持します。そのため、呼び出し主はセッションキャッシュフラグのみを保持します。 行をロードする必要はありません。 プッシュが安全: すでにクラスターと12の1つに存在する列の両方 組織は偽りを読みます。 着陸前の情報 schemaに対して検証 — handles phi boolean NULL DEFAULT false, phi effective date timestamp NULL. 再現可能な形態のカモイニタイザマイグレーションを参照してください.

  26. · kamo-shared-library

    PhiTenantGuardとモジュールのコンプライアンス境界

    HIPAA準拠境界の鍵盤: テナントが到達するモジュール テナントが保護された健康情報を扱うかどうかを与えられた。 お問い合わせ MFAが強制的であるかどうか、統合が無効化されるダウンストリームキーオフ — どのセッションが厳密なタイムアウトを得るか、監査のスピンはカバーしなければなりません。 スキーマの依存性を心配しないでください。 以前の試みは、この新しいオフを掛けました Organization.handlesPhi 列と再変換されなければならない: サービス CI クローン KamoInitializerが手によって動く間、造りの時間で共有ライブラリの本管、従って メインのエンティティティ列は、次のサービス展開を誰かのマイグレートまで中断します。 組織からポリシーを解凍すると、それが土地になり、今施行することができます。 コラムは独立したステップになります。 3つの特性は設計のまわりで造られます: - 失敗は閉鎖しました。 PhiModuleのコンストラクタはPhiDispositionと理由が必要です。 従ってモジュールは後でomissionによって許可を継承できません加えました-それはそうしません 決定せずにコンパイルし、PERMITTED denies 以外のもの。 です。 障害モード コンプライアンス境界が生き残る必要があります: モジュールの船、誰も クリニックはPHIについて考えています。 - 未解決のテナントは安全なテナントではありません。 ガードはトライステートを取ります PhiTenantStatus ではなく boolean なので "組織をロードできません" 「この組織はPHIではない」と同じ方法では綴じられません。 お問い合わせ boolean、エラーパスはisPermitted(false、...)です。 レビューで見られない。 UNKNOWNは、すべてを拒否します. - 何も上昇しません。 決定は(status、モジュール)の純粋な機能であり、 呼び出し側、セッション、右またはGod-modeパラメータ。 「当社のサポートスタッフは、それを有効にすることができます off"は、被ったエンティティティの見直しに失敗する答えです。 分解はkamo内部のスキャッピング分析に従います メニュー 許可:CRM、連絡先、文書のボルト、 セルフホストメール、電子署名、メモ、タスク、カレンダー、内部チャット、インクルーザー トランスクリプション/トランスレーションスタック、MLOS。 取得可能なBAAなしでブロック:社会的 メッセージング、カンバ、電子商取引同期、MCPゲートウェイ。 ブロックされた保留中の作業: AIルーター、RAG検索、会議録画、VOIP録画、マーケティングキャンペーン、 分析エクスポート。 ガードを呼びませんが、これは配線されるまで不活性です.

  27. · InitializerService

    HANDLES PHIとPHI EFFECTIVE DATEをorgsで作成

    Kamo-shared-library の組織に 2 つの新しいフィールドをミラーリングします。 OrgBrandingFieldsMigration 規則に従う。 HANDLES PHIはNULLではありません DEFAULT FALSE は、既存のテナントがコンプライアンスの外部に明示的に サイレントな内部ではなく境界。 PHI EFFECTIVE DATE は TIMESTAMP ではなく TIMESTAMPTZ は、エンティティティティティティティティティエンティティティエントタイムであるためです。 SEQUENCING — 次のサービスデプロイの前に実行する必要があります。 サービス ピン kamo-shared-library は、再出版される固定版で、最初に そのライブラリの変更がこれらの列を選択した後に再構築するサービス このランナーが実行されるまで失敗するサービスで ddl-auto:none を使って。 同行テストはドリフトガードで、休息はありません。列を読みます リフレクションでエンティティティティをオフし、エンティティティフィールドが存在しない場合、ビルドに失敗します お問い合わせ これは、このすべての慣習が持っている障害モードです。誰かがフィールドを追加します。 マイグレーションを忘れて、その代わりにプロダクションクエリエラーとして表します。 ビルド時間。 変異による検証: PHI EFFECTIVE DATE ALTER を外すことで、 アクション可能なメッセージで失敗し、それを回復すると緑色になります.

  28. · ESigService

    署名のリンクで署名者のロケールセグメントを省略

    バイト単位の重複したビルドのSignLinkをSignLinkBuilderに抽出 組織の言語からソースされた locale セグメントを追加します。 翻訳なしで4つの LanguageType 値の英語に戻ります。 kamo-signer /{locale}/ routing を最初にデプロイする必要があります.

  29. · kamo-shared-library

    ハンドルを追加PhiテナントフラグとPhiTenantGuard

    HIPAA準拠境界の鍵盤: 組織ごとのフラグと、 組織が到達するモジュールを監視します。 すべてのダウンストリーム キーオフ — MFA が強制的なかどうか、その統合が無効になっています。 セッションは、厳しいタイムアウトを得る, 監査の背骨がカバーしなければならないもの. 組織は HANDLES PHI (BOOL DEFAULT FALSE) と PHI EFFECTIVE DATE を獲得しました。 既存の**************** パターンに従って。 デフォルト false, そのため、これは既存の住宅ローンとCRMブックのインサートです。 3つの特性は設計のまわりで造られます: - 失敗は閉鎖しました。 PhiModuleのコンストラクタはPhiDispositionと理由が必要です。 従ってモジュールは後でomissionによって許可を継承できません加えました-それはそうしません 決定せずにコンパイルし、PERMITTED denies 以外のもの。 です。 特定の障害モードは、コンプライアンス境界が生き残る必要があります:モジュール船、 クリニックでは、PHIについて誰も考えていません。 - バイパスできません。 決定は(組織化、モジュール)の純粋な機能であり、 呼び出し者、セッション、右、または神モードのパラメーターがないので、 上昇。 「サポートスタッフはオフにすることができます」は、カバーが失敗する答えです 企業のレビュー。 - Denialsはサポートによって「拒否」が利用できないので、理由を運びます エンジニアまたは顧客。 分解はkamo内部のスキャッピング分析に従います メニュー 今日の許可:CRM、連絡先、文書 ボルト、自己ホストされた電子メール、電子署名、メモ、タスク、カレンダー、内部チャット、 in-cluster トランスクリプション/トランスレーションスタック、および MLOS。 取得可能無しで妨げられる BAA:社会的メッセージング、カンバ、電子商取引同期、MCPゲートウェイ。 ブロックされた保留名 仕事:AIのルーター、RAGの調査、会議の録音、VOIPの録音、マーケティング キャンペーン、分析エクスポート。 スキーマメモ: HANDLES PHI と PHI EFFECTIVE DATE は、次のようになります。 KamoInitializer 実行後、CI がこの共有ライブラリビルドを公開します。 何も読みません まだフラグなので、注文は安全です.

  30. · kamo-signer-monorepo

    Locale-aware 日付のフォーマットと/-localeメタデータ

君の宇宙は待ってる

数分で無料で開始できます。準備ができたらいつでも製品を追加できます。移行も新しいログインも不要です。

クレジットカード不要